测试 glob 在 Bash 中是否有任何匹配项
如果我想检查单个文件是否存在,可以使用 test -e filename
或 [ -e filename ]
进行测试。
假设我有一个 glob,我想知道是否存在名称与 glob 匹配的文件。 glob 可以匹配 0 个文件(在这种情况下我不需要执行任何操作),也可以匹配 1 个或多个文件(在这种情况下我需要执行某些操作)。如何测试 glob 是否有任何匹配项? (我不在乎有多少匹配项,如果我可以使用一个 if
语句并且没有循环来完成此操作,那就最好了(只是因为我发现它最具可读性)。
(如果 glob 匹配多个文件,则 test -e glob*
会失败。)
If I want to check for the existence of a single file, I can test for it using test -e filename
or [ -e filename ]
.
Supposing I have a glob and I want to know whether any files exist whose names match the glob. The glob can match 0 files (in which case I need to do nothing), or it can match 1 or more files (in which case I need to do something). How can I test whether a glob has any matches? (I don't care how many matches there are, and it would be best if I could do this with one if
statement and no loops (simply because I find that most readable).
(test -e glob*
fails if the glob matches more than one file.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(22)
Bash 特定的解决方案:
转义模式,否则它将被预先扩展为匹配项。
退出状态为:
stdout
是与 glob 匹配的文件列表。我认为就简洁性和最小化潜在副作用而言,这是最好的选择。
例子:
Bash-specific solution:
Escape the pattern or it'll get pre-expanded into matches.
Exit status is:
stdout
is a list of files matching the glob.I think this is the best option in terms of conciseness and minimizing potential side effects.
Example:
nullglob shell 选项确实是一个 bashism。
为了避免繁琐的保存和恢复 nullglob 状态的需要,我只将其设置在扩展 glob 的子 shell 中:
为了更好的可移植性和更灵活的 globbing,请使用 find:
显式 -print -quit 操作用于 find,而不是默认的隐式 -print 操作,以便 find 一旦找到第一个匹配的文件就会退出搜索条件。当大量文件匹配时,这应该比 echo glob* 或 ls glob* 运行得快得多,并且还避免了过度填充扩展命令行的可能性(某些 shell 有一个4K 长度限制)。
如果 find 感觉有点大材小用,并且可能匹配的文件数量很少,请使用 stat:
The nullglob shell option is indeed a bashism.
To avoid the need for a tedious save and restore of the nullglob state, I'd only set it inside the subshell that expands the glob:
For better portability and more flexible globbing, use find:
Explicit -print -quit actions are used for find instead of the default implicit -print action so that find will quit as soon as it finds the first file matching the search criteria. Where lots of files match, this should run much faster than
echo glob*
orls glob*
and it also avoids the possibility of overstuffing the expanded command line (some shells have a 4K length limit).If find feels like overkill and the number of files likely to match is small, use stat:
我喜欢
这既可读又高效(除非有大量文件)。
主要缺点是它比看起来要微妙得多,有时我觉得有必要添加很长的评论。
如果存在匹配项,
"glob*"
将由 shell 展开,并将所有匹配项传递给exists()
,后者检查第一个匹配项并忽略其余匹配项。< br>如果没有匹配,
"glob*"
会被传递给exists()
,并且发现那里也不存在。编辑:可能存在误报,请参阅 评论< /a>
I like
This is both readable and efficient (unless there are a huge number of files).
The main drawback is that it's much more subtle than it looks, and I sometimes feel compelled to add a long comment.
If there's a match,
"glob*"
is expanded by the shell and all the matches are passed toexists()
, which checks the first one and ignores the rest.If there's no match,
"glob*"
is passed toexists()
and found not to exist there either.Edit: there may be a false positive, see comment
如果你设置了 globfail 你可以使用这个疯狂的(你真的不应该)
或者
If you have globfail set you can use this crazy (which you really should not)
or
test -e 有一个不幸的警告,它认为损坏的符号链接不存在。所以您可能也想检查一下。
test -e has the unfortunate caveat that it considers broken symbolic links to not exist. So you may want to check for those, too.
我还有另一个解决方案:
这对我来说效果很好。我可能错过了一些极端情况。
I have yet another solution:
This works nicely for me. There may be some corner cases I missed.
根据 flabdablet 的答案,对我来说,看起来最简单(不一定是最快)就是使用 find 本身,同时在 shell 上保留 glob 扩展,例如:
或者在
if
中,例如:Based on flabdablet's answer, for me it looks like easiest (not necessarily fastest) is just to use find itself, while leaving glob expansion on shell, like:
Or in
if
like:为了稍微简化 miku 的答案,根据他的想法:
To simplify miku's answer somewhat, based on his idea:
在 Bash 中,您可以通配到数组;如果 glob 不匹配,您的数组将包含一个与现有文件不对应的条目:
注意:如果您设置了
nullglob
,则scripts
将是一个空数组,您应该使用[ "${scripts[*]}" ]
或[ "${#scripts[*]}" != 0 ]
相反。如果您正在编写一个必须使用或不使用nullglob
的库,那么您将需要这种方法的优点是您可以获得要使用的文件列表,而不必重复全局操作。
In Bash, you can glob to an array; if the glob didn't match, your array will contain a single entry that doesn't correspond to an existing file:
Note: if you have
nullglob
set,scripts
will be an empty array, and you should test with[ "${scripts[*]}" ]
or with[ "${#scripts[*]}" != 0 ]
instead. If you're writing a library that must work with or withoutnullglob
, you'll wantAn advantage of this approach is that you then have the list of files you want to work with, rather than having to repeat the glob operation.
如果要在迭代文件之前测试文件是否存在,可以使用此模式:
如果 glob 不匹配任何内容,则 $F 将是非扩展 glob(在本例中为“glob*”),并且如果不存在同名文件,它将跳过循环的其余部分。
If you want to test if the files exist before iterating over them, you can use this pattern:
if the glob does not does not match anything, $F will be the non-expanded glob ('glob*' in this case) and if a file with the same name does not exist, it will skip the rest of the loop.
说明
当
glob*
不匹配时,$1
将包含'glob*'
。测试-f "$1"
不会为 true,因为glob*
文件不存在。为什么这比替代方案更好
这适用于 sh 及其衍生产品:KornShell 和 Bash。它不会创建任何子 shell。
$(..)
和`...`
命令创建一个子 shell;他们分叉了一个进程,因此比这个解决方案慢。Explanation
When there isn't a match for
glob*
, then$1
will contain'glob*'
. The test-f "$1"
won't be true because theglob*
file doesn't exist.Why this is better than alternatives
This works with sh and derivates: KornShell and Bash. It doesn't create any sub-shell.
$(..)
and`...`
commands create a sub-shell; they fork a process, and therefore are slower than this solution.就像 Bash 中的这样(包含
pattern
的测试文件):它比
compgen 好得多-G
:因为我们可以区分更多的情况,更精确。它只能与一个通配符
*
一起使用。Like this in Bash (test files containing
pattern
):It's far better than
compgen -G
: because we can discriminates more cases and more precisely.It can work with only one wildcard
*
.这个令人厌恶的东西似乎起作用了:
它可能需要 bash,而不是 sh。
这是有效的,因为如果没有匹配项,nullglob 选项会导致 glob 计算为空字符串。因此,echo 命令的任何非空输出都表明 glob 与某些内容匹配。
This abomination seems to work:
It probably requires bash, not sh.
This works because the nullglob option causes the glob to evaluate to an empty string if there are no matches. Thus any non-empty output from the echo command indicates that the glob matched something.
Bash 中扩展 glob (
extglob
) 的一种解决方案:至少有一个匹配:
使用的概念:
ls
' 退出代码行为(为 效率,以及-1
用于输出控制)。$
前缀来解释\n
,以便扩展 glob 模式与shopt -s extglob
位于不同的行上code>——否则扩展的 glob 模式将是语法错误!注 1: 我致力于此解决方案,因为其他答案中建议的
compgen -G ""
方法似乎无法与 大括号扩展;但我需要一些更高级的通配符功能。注2:扩展 glob 语法的可爱资源:extglob
shopt -s extglob \n /bin/ls -1U <ext-glob-pattern>'如果至少有一个匹配,则退出状态为 0;如果没有匹配,则退出状态为非零 (2)。标准输出包含以换行符分隔的匹配文件列表(以及包含引用的空格的文件名)。
或者,稍微不同:
与基于 ls 的解决方案的差异:可能更快(未测量),输出中未引用空格的文件名,当没有引用时退出代码 1匹配(不是 2:耸肩:)。
用法示例:
不匹配:
至少有一个匹配:
使用的概念:
ls
' 退出代码行为(为 效率,以及-1
用于输出控制)。$
前缀来解释\n
,以便扩展 glob 模式与shopt -s extglob
位于不同的行上code>——否则扩展的 glob 模式将是语法错误!注 1: 我致力于此解决方案,因为其他答案中建议的
compgen -G ""
方法似乎无法与 大括号扩展;但我需要一些更高级的通配符功能。注2:扩展 glob 语法的可爱资源:extglob
shopt -s extglob \n compgen -G <ext-glob-pattern>'与基于 ls 的解决方案的差异:可能更快(未测量),输出中未引用空格的文件名,当没有引用时退出代码 1匹配(不是 2:耸肩:)。
用法示例:
不匹配:
至少有一个匹配:
使用的概念:
ls
' 退出代码行为(为 效率,以及-1
用于输出控制)。$
前缀来解释\n
,以便扩展 glob 模式与shopt -s extglob
位于不同的行上code>——否则扩展的 glob 模式将是语法错误!注 1: 我致力于此解决方案,因为其他答案中建议的
compgen -G ""
方法似乎无法与 大括号扩展;但我需要一些更高级的通配符功能。注2:扩展 glob 语法的可爱资源:extglob
shopt -s extglob \n /bin/ls -1U <ext-glob-pattern>'如果至少有一个匹配,则退出状态为 0;如果没有匹配,则退出状态为非零 (2)。标准输出包含以换行符分隔的匹配文件列表(以及包含引用的空格的文件名)。
或者,稍微不同:
与基于 ls 的解决方案的差异:可能更快(未测量),输出中未引用空格的文件名,当没有引用时退出代码 1匹配(不是 2:耸肩:)。
用法示例:
不匹配:
至少有一个匹配:
使用的概念:
ls
' 退出代码行为(为 效率,以及-1
用于输出控制)。$
前缀来解释\n
,以便扩展 glob 模式与shopt -s extglob
位于不同的行上code>——否则扩展的 glob 模式将是语法错误!注 1: 我致力于此解决方案,因为其他答案中建议的
compgen -G ""
方法似乎无法与 大括号扩展;但我需要一些更高级的通配符功能。注2:扩展 glob 语法的可爱资源:extglob
shopt -s extglob \n /bin/ls -1U @(*.ts|*.mp4)'; echo "exit status: $?" 'video1 with spaces.mp4' video2.mp4 video3.mp4 exit status: 0使用的概念:
ls
' 退出代码行为(为 效率,以及-1
用于输出控制)。$
前缀来解释\n
,以便扩展 glob 模式与shopt -s extglob
位于不同的行上code>——否则扩展的 glob 模式将是语法错误!注 1: 我致力于此解决方案,因为其他答案中建议的
compgen -G ""
方法似乎无法与 大括号扩展;但我需要一些更高级的通配符功能。注2:扩展 glob 语法的可爱资源:extglob
shopt -s extglob \n /bin/ls -1U <ext-glob-pattern>'如果至少有一个匹配,则退出状态为 0;如果没有匹配,则退出状态为非零 (2)。标准输出包含以换行符分隔的匹配文件列表(以及包含引用的空格的文件名)。
或者,稍微不同:
与基于 ls 的解决方案的差异:可能更快(未测量),输出中未引用空格的文件名,当没有引用时退出代码 1匹配(不是 2:耸肩:)。
用法示例:
不匹配:
至少有一个匹配:
使用的概念:
ls
' 退出代码行为(为 效率,以及-1
用于输出控制)。$
前缀来解释\n
,以便扩展 glob 模式与shopt -s extglob
位于不同的行上code>——否则扩展的 glob 模式将是语法错误!注 1: 我致力于此解决方案,因为其他答案中建议的
compgen -G ""
方法似乎无法与 大括号扩展;但我需要一些更高级的通配符功能。注2:扩展 glob 语法的可爱资源:extglob
shopt -s extglob \n compgen -G <ext-glob-pattern>'与基于 ls 的解决方案的差异:可能更快(未测量),输出中未引用空格的文件名,当没有引用时退出代码 1匹配(不是 2:耸肩:)。
用法示例:
不匹配:
至少有一个匹配:
使用的概念:
ls
' 退出代码行为(为 效率,以及-1
用于输出控制)。$
前缀来解释\n
,以便扩展 glob 模式与shopt -s extglob
位于不同的行上code>——否则扩展的 glob 模式将是语法错误!注 1: 我致力于此解决方案,因为其他答案中建议的
compgen -G ""
方法似乎无法与 大括号扩展;但我需要一些更高级的通配符功能。注2:扩展 glob 语法的可爱资源:extglob
shopt -s extglob \n /bin/ls -1U <ext-glob-pattern>'如果至少有一个匹配,则退出状态为 0;如果没有匹配,则退出状态为非零 (2)。标准输出包含以换行符分隔的匹配文件列表(以及包含引用的空格的文件名)。
或者,稍微不同:
与基于 ls 的解决方案的差异:可能更快(未测量),输出中未引用空格的文件名,当没有引用时退出代码 1匹配(不是 2:耸肩:)。
用法示例:
不匹配:
至少有一个匹配:
使用的概念:
ls
' 退出代码行为(为 效率,以及-1
用于输出控制)。$
前缀来解释\n
,以便扩展 glob 模式与shopt -s extglob
位于不同的行上code>——否则扩展的 glob 模式将是语法错误!注 1: 我致力于此解决方案,因为其他答案中建议的
compgen -G ""
方法似乎无法与 大括号扩展;但我需要一些更高级的通配符功能。注2:扩展 glob 语法的可爱资源:extglob
shopt -s extglob \n /bin/ls -1U @(*.foo|*.bar)'; echo "exit status: $?" /bin/ls: cannot access '@(*.foo|*.bar)': No such file or directory exit status: 2至少有一个匹配:
使用的概念:
ls
' 退出代码行为(为 效率,以及-1
用于输出控制)。$
前缀来解释\n
,以便扩展 glob 模式与shopt -s extglob
位于不同的行上code>——否则扩展的 glob 模式将是语法错误!注 1: 我致力于此解决方案,因为其他答案中建议的
compgen -G ""
方法似乎无法与 大括号扩展;但我需要一些更高级的通配符功能。注2:扩展 glob 语法的可爱资源:extglob
shopt -s extglob \n /bin/ls -1U <ext-glob-pattern>'如果至少有一个匹配,则退出状态为 0;如果没有匹配,则退出状态为非零 (2)。标准输出包含以换行符分隔的匹配文件列表(以及包含引用的空格的文件名)。
或者,稍微不同:
与基于 ls 的解决方案的差异:可能更快(未测量),输出中未引用空格的文件名,当没有引用时退出代码 1匹配(不是 2:耸肩:)。
用法示例:
不匹配:
至少有一个匹配:
使用的概念:
ls
' 退出代码行为(为 效率,以及-1
用于输出控制)。$
前缀来解释\n
,以便扩展 glob 模式与shopt -s extglob
位于不同的行上code>——否则扩展的 glob 模式将是语法错误!注 1: 我致力于此解决方案,因为其他答案中建议的
compgen -G ""
方法似乎无法与 大括号扩展;但我需要一些更高级的通配符功能。注2:扩展 glob 语法的可爱资源:extglob
shopt -s extglob \n compgen -G <ext-glob-pattern>'与基于 ls 的解决方案的差异:可能更快(未测量),输出中未引用空格的文件名,当没有引用时退出代码 1匹配(不是 2:耸肩:)。
用法示例:
不匹配:
至少有一个匹配:
使用的概念:
ls
' 退出代码行为(为 效率,以及-1
用于输出控制)。$
前缀来解释\n
,以便扩展 glob 模式与shopt -s extglob
位于不同的行上code>——否则扩展的 glob 模式将是语法错误!注 1: 我致力于此解决方案,因为其他答案中建议的
compgen -G ""
方法似乎无法与 大括号扩展;但我需要一些更高级的通配符功能。注2:扩展 glob 语法的可爱资源:extglob
shopt -s extglob \n /bin/ls -1U <ext-glob-pattern>'如果至少有一个匹配,则退出状态为 0;如果没有匹配,则退出状态为非零 (2)。标准输出包含以换行符分隔的匹配文件列表(以及包含引用的空格的文件名)。
或者,稍微不同:
与基于 ls 的解决方案的差异:可能更快(未测量),输出中未引用空格的文件名,当没有引用时退出代码 1匹配(不是 2:耸肩:)。
用法示例:
不匹配:
至少有一个匹配:
使用的概念:
ls
' 退出代码行为(为 效率,以及-1
用于输出控制)。$
前缀来解释\n
,以便扩展 glob 模式与shopt -s extglob
位于不同的行上code>——否则扩展的 glob 模式将是语法错误!注 1: 我致力于此解决方案,因为其他答案中建议的
compgen -G ""
方法似乎无法与 大括号扩展;但我需要一些更高级的通配符功能。注2:扩展 glob 语法的可爱资源:extglob
A solution for extended globs (
extglob
) in Bash:At least one match:
Concepts used:
ls
' exit code behavior (adds-U
for efficiency, and-1
for output control).extglob
in current shell (often not desired).$
prefix so that the\n
is interpreted, so that the extended glob pattern is on a different line than theshopt -s extglob
-- otherwise the extended glob pattern would be a syntax error!Note 1: I worked towards this solution because the
compgen -G "<glob-pattern>"
approach suggested in other answers does not seem to work smoothly with brace expansion; and yet I needed some more advanced globbing features.Note 2: lovely resource for the extended glob syntax: extglob
shopt -s extglob \n /bin/ls -1U <ext-glob-pattern>'Exit status is 0 if there is at least one match, and non-zero (2) when there is no match. Standard output contains a newline-separated list of matching files (and file names containing spaces they are quoted).
Or, slightly different:
Differences to the
ls
-based solution: probably faster (not measured), file names with spaces not quoted in output, exit code 1 when there is no match (not 2 :shrug:).Example usage:
No match:
At least one match:
Concepts used:
ls
' exit code behavior (adds-U
for efficiency, and-1
for output control).extglob
in current shell (often not desired).$
prefix so that the\n
is interpreted, so that the extended glob pattern is on a different line than theshopt -s extglob
-- otherwise the extended glob pattern would be a syntax error!Note 1: I worked towards this solution because the
compgen -G "<glob-pattern>"
approach suggested in other answers does not seem to work smoothly with brace expansion; and yet I needed some more advanced globbing features.Note 2: lovely resource for the extended glob syntax: extglob
shopt -s extglob \n compgen -G <ext-glob-pattern>'Differences to the
ls
-based solution: probably faster (not measured), file names with spaces not quoted in output, exit code 1 when there is no match (not 2 :shrug:).Example usage:
No match:
At least one match:
Concepts used:
ls
' exit code behavior (adds-U
for efficiency, and-1
for output control).extglob
in current shell (often not desired).$
prefix so that the\n
is interpreted, so that the extended glob pattern is on a different line than theshopt -s extglob
-- otherwise the extended glob pattern would be a syntax error!Note 1: I worked towards this solution because the
compgen -G "<glob-pattern>"
approach suggested in other answers does not seem to work smoothly with brace expansion; and yet I needed some more advanced globbing features.Note 2: lovely resource for the extended glob syntax: extglob
shopt -s extglob \n /bin/ls -1U <ext-glob-pattern>'Exit status is 0 if there is at least one match, and non-zero (2) when there is no match. Standard output contains a newline-separated list of matching files (and file names containing spaces they are quoted).
Or, slightly different:
Differences to the
ls
-based solution: probably faster (not measured), file names with spaces not quoted in output, exit code 1 when there is no match (not 2 :shrug:).Example usage:
No match:
At least one match:
Concepts used:
ls
' exit code behavior (adds-U
for efficiency, and-1
for output control).extglob
in current shell (often not desired).$
prefix so that the\n
is interpreted, so that the extended glob pattern is on a different line than theshopt -s extglob
-- otherwise the extended glob pattern would be a syntax error!Note 1: I worked towards this solution because the
compgen -G "<glob-pattern>"
approach suggested in other answers does not seem to work smoothly with brace expansion; and yet I needed some more advanced globbing features.Note 2: lovely resource for the extended glob syntax: extglob
shopt -s extglob \n /bin/ls -1U @(*.ts|*.mp4)'; echo "exit status: $?" 'video1 with spaces.mp4' video2.mp4 video3.mp4 exit status: 0Concepts used:
ls
' exit code behavior (adds-U
for efficiency, and-1
for output control).extglob
in current shell (often not desired).$
prefix so that the\n
is interpreted, so that the extended glob pattern is on a different line than theshopt -s extglob
-- otherwise the extended glob pattern would be a syntax error!Note 1: I worked towards this solution because the
compgen -G "<glob-pattern>"
approach suggested in other answers does not seem to work smoothly with brace expansion; and yet I needed some more advanced globbing features.Note 2: lovely resource for the extended glob syntax: extglob
shopt -s extglob \n /bin/ls -1U <ext-glob-pattern>'Exit status is 0 if there is at least one match, and non-zero (2) when there is no match. Standard output contains a newline-separated list of matching files (and file names containing spaces they are quoted).
Or, slightly different:
Differences to the
ls
-based solution: probably faster (not measured), file names with spaces not quoted in output, exit code 1 when there is no match (not 2 :shrug:).Example usage:
No match:
At least one match:
Concepts used:
ls
' exit code behavior (adds-U
for efficiency, and-1
for output control).extglob
in current shell (often not desired).$
prefix so that the\n
is interpreted, so that the extended glob pattern is on a different line than theshopt -s extglob
-- otherwise the extended glob pattern would be a syntax error!Note 1: I worked towards this solution because the
compgen -G "<glob-pattern>"
approach suggested in other answers does not seem to work smoothly with brace expansion; and yet I needed some more advanced globbing features.Note 2: lovely resource for the extended glob syntax: extglob
shopt -s extglob \n compgen -G <ext-glob-pattern>'Differences to the
ls
-based solution: probably faster (not measured), file names with spaces not quoted in output, exit code 1 when there is no match (not 2 :shrug:).Example usage:
No match:
At least one match:
Concepts used:
ls
' exit code behavior (adds-U
for efficiency, and-1
for output control).extglob
in current shell (often not desired).$
prefix so that the\n
is interpreted, so that the extended glob pattern is on a different line than theshopt -s extglob
-- otherwise the extended glob pattern would be a syntax error!Note 1: I worked towards this solution because the
compgen -G "<glob-pattern>"
approach suggested in other answers does not seem to work smoothly with brace expansion; and yet I needed some more advanced globbing features.Note 2: lovely resource for the extended glob syntax: extglob
shopt -s extglob \n /bin/ls -1U <ext-glob-pattern>'Exit status is 0 if there is at least one match, and non-zero (2) when there is no match. Standard output contains a newline-separated list of matching files (and file names containing spaces they are quoted).
Or, slightly different:
Differences to the
ls
-based solution: probably faster (not measured), file names with spaces not quoted in output, exit code 1 when there is no match (not 2 :shrug:).Example usage:
No match:
At least one match:
Concepts used:
ls
' exit code behavior (adds-U
for efficiency, and-1
for output control).extglob
in current shell (often not desired).$
prefix so that the\n
is interpreted, so that the extended glob pattern is on a different line than theshopt -s extglob
-- otherwise the extended glob pattern would be a syntax error!Note 1: I worked towards this solution because the
compgen -G "<glob-pattern>"
approach suggested in other answers does not seem to work smoothly with brace expansion; and yet I needed some more advanced globbing features.Note 2: lovely resource for the extended glob syntax: extglob
shopt -s extglob \n /bin/ls -1U @(*.foo|*.bar)'; echo "exit status: $?" /bin/ls: cannot access '@(*.foo|*.bar)': No such file or directory exit status: 2At least one match:
Concepts used:
ls
' exit code behavior (adds-U
for efficiency, and-1
for output control).extglob
in current shell (often not desired).$
prefix so that the\n
is interpreted, so that the extended glob pattern is on a different line than theshopt -s extglob
-- otherwise the extended glob pattern would be a syntax error!Note 1: I worked towards this solution because the
compgen -G "<glob-pattern>"
approach suggested in other answers does not seem to work smoothly with brace expansion; and yet I needed some more advanced globbing features.Note 2: lovely resource for the extended glob syntax: extglob
shopt -s extglob \n /bin/ls -1U <ext-glob-pattern>'Exit status is 0 if there is at least one match, and non-zero (2) when there is no match. Standard output contains a newline-separated list of matching files (and file names containing spaces they are quoted).
Or, slightly different:
Differences to the
ls
-based solution: probably faster (not measured), file names with spaces not quoted in output, exit code 1 when there is no match (not 2 :shrug:).Example usage:
No match:
At least one match:
Concepts used:
ls
' exit code behavior (adds-U
for efficiency, and-1
for output control).extglob
in current shell (often not desired).$
prefix so that the\n
is interpreted, so that the extended glob pattern is on a different line than theshopt -s extglob
-- otherwise the extended glob pattern would be a syntax error!Note 1: I worked towards this solution because the
compgen -G "<glob-pattern>"
approach suggested in other answers does not seem to work smoothly with brace expansion; and yet I needed some more advanced globbing features.Note 2: lovely resource for the extended glob syntax: extglob
shopt -s extglob \n compgen -G <ext-glob-pattern>'Differences to the
ls
-based solution: probably faster (not measured), file names with spaces not quoted in output, exit code 1 when there is no match (not 2 :shrug:).Example usage:
No match:
At least one match:
Concepts used:
ls
' exit code behavior (adds-U
for efficiency, and-1
for output control).extglob
in current shell (often not desired).$
prefix so that the\n
is interpreted, so that the extended glob pattern is on a different line than theshopt -s extglob
-- otherwise the extended glob pattern would be a syntax error!Note 1: I worked towards this solution because the
compgen -G "<glob-pattern>"
approach suggested in other answers does not seem to work smoothly with brace expansion; and yet I needed some more advanced globbing features.Note 2: lovely resource for the extended glob syntax: extglob
shopt -s extglob \n /bin/ls -1U <ext-glob-pattern>'Exit status is 0 if there is at least one match, and non-zero (2) when there is no match. Standard output contains a newline-separated list of matching files (and file names containing spaces they are quoted).
Or, slightly different:
Differences to the
ls
-based solution: probably faster (not measured), file names with spaces not quoted in output, exit code 1 when there is no match (not 2 :shrug:).Example usage:
No match:
At least one match:
Concepts used:
ls
' exit code behavior (adds-U
for efficiency, and-1
for output control).extglob
in current shell (often not desired).$
prefix so that the\n
is interpreted, so that the extended glob pattern is on a different line than theshopt -s extglob
-- otherwise the extended glob pattern would be a syntax error!Note 1: I worked towards this solution because the
compgen -G "<glob-pattern>"
approach suggested in other answers does not seem to work smoothly with brace expansion; and yet I needed some more advanced globbing features.Note 2: lovely resource for the extended glob syntax: extglob
nullglob
和compgen
仅在某些 bash shell 上有用。适用于大多数 shell 的(非递归)解决方案是:
Both
nullglob
andcompgen
are useful only on some bash shells.A (non-recursive) solution that works on most shells is:
假设您可能想要对存在的文件执行某些操作:
如果您需要对文件执行某些操作,则可以循环访问 exists 数组。
Assuming you may want to do something with the files if they exist:
You can then loop through the exists array if you need to do something with the files.
我想提出一个低技术解决方案:
这依赖于 ls 的退出代码:如果文件不存在,则它是非零(即指示错误):
让我们假设我们有文件
glob1
和glob2
。然后ls -d glob*
扩展为ls -d glob1 glob2
,并且ls
返回零退出代码(即成功)。假设不存在这样的文件。然后
ls -d glob*
扩展为ls -d glob*
,它不是现有文件,因此ls
返回一个非 -零退出代码(即失败)。不幸的是,当您打开
failglob
或nullglob
时,此功能将停止工作。I would like to propose a low-tech solution:
This relies on the exit code of
ls
: it is non-zero (i.e., indicates an error) if the file(s) did not exist:Let's say we have files
glob1
andglob2
. Thenls -d glob*
expands tols -d glob1 glob2
, andls
returns with a zero exit code (i.e., success).Let's say that no such file exists. Then
ls -d glob*
expands tols -d glob*
, which is not an existing file, and thereforels
returns with a non-zero exit code (i.e., failure).Unfortunately, this stops working when you turn on
failglob
ornullglob
.如果您的目标是“获取文件列表,每行一个,我可以将其提供给其他函数/进程”,一个适当的可移植选项是
printf
。如果没有匹配项,
printf '%s\n' the_glob*
将不打印任何内容,否则在换行符上打印每个项目,适合管道或进程重定向:首选用法:将项目读入数组,以很好地处理分词和空结果:
readarray -t the_items
readarray -t the_items
readarray -t the_items
readarray -t the_items
readarray -t the_items < <(printf '%s\n' the_glob*)
在此示例中,您现在拥有可以处理
"${the_items[@]:-safely}"
的匹配项数组
(如果没有匹配的内容,前面的示例将扩展为
“safely”
)您还可以检查大小:
if (( ${#the_items[@]} > 0 ))
或循环元素:for item in "${the_items[@]}"< /code>
您可以做不太安全的事情,例如
items="$(printf ...)"
,但随后您会遇到分词问题。如果你真的想将它分配给一个字符串,
printf -v
(仅限bash / new zsh)可以避免子shell,只是要小心,因为它会在末尾附加一个额外的换行符:对于带有glob的变量在其中添加一些 eval:
eval "printf '%s\n' ${variable_with_globs}"
我更喜欢 printf 而不是替代方案,原因如下:
ls
/stat
- 分词、空格分隔find
- 需要非 POSIX 选项adm
用户,只需从/etc/passwd
中选择您自己或 root 以外的用户):( cd ~ ; sudo -u adm find / -maxdepth 1 ; echo $? )
adm
无法“返回”我的家,因此发现失败,退出代码为 1shopt
- 我不想弄乱 shell 状态test
/[
/[[< /code> - 确定存在和过程值的两步过程。如果您不知道如何填充数组并传递它,这可能很有用(
nameref
是您传递数组变量的朋友)printf
是可移植的(没有非 POSIX)所需的选项(与 find 不同)可以显式地与换行符(或您想要的任何内容)分隔,并且如果 glob 不匹配任何内容,则不打印任何内容。使用数组是可选的,但通常是最安全的选择。避免使用printf -v
来处理全局变量。If your goal is "get a list of files, one per line, that I can feed to some other function / process", a decently portable option is
printf
.printf '%s\n' the_glob*
will print nothing if there are no matches, otherwise prints each item on a newline, suitable for pipes or process redirection:Preferred usage: read items into an array, to handle wordsplitting and empty results nicely:
readarray -t the_items < <(printf '%s\n' the_glob*)
In this example, you now have an array of matched items you can handle
"${the_items[@]:-safely}"
(if nothing matched, the previous example would expand to
"safely"
)You can also check size:
if (( ${#the_items[@]} > 0 ))
or loop over elements:for item in "${the_items[@]}"
You can do less safe things, like
items="$(printf ...)"
, but then you'll hit word splitting problems.If you really want to assign it to a string,
printf -v
(bash / new zsh only) can avoid subshells, just be careful as it will append an extra newline at the end:For variables with globs in them, add a sprinkle of eval:
eval "printf '%s\n' ${variable_with_globs}"
I prefer printf over the alternatives for the following reasons:
ls
/stat
- word splitting, space separatedfind
- requires non-POSIX optionsadm
user, just pick a user other than your own or root from/etc/passwd
):( cd ~ ; sudo -u adm find / -maxdepth 1 ; echo $? )
adm
cannot "return" to my home, so find fails with exit code 1shopt
- I don't want to mess with shell statetest
/[
/[[
- two step process to determine existence and process values. Maybe useful if you don't know how to just fill an array and pass it around (nameref
is your friend for passing array variables)printf
is portable (no non-POSIX options needed, unlike find), can explicitly separate with newlines (or whatever you want), and prints nothing if the glob matches nothing. Using arrays is optional, but generally safest option. Avoidprintf -v
for globs.ls | grep -q "glob.*"
不是最有效的解决方案(如果目录中有大量文件,它可能会很慢),但它很简单,易于阅读,并且还具有正则表达式更强大的优点与普通的 Bash glob 模式相比。
ls | grep -q "glob.*"
Not the most efficient solution (if there's a ton of files in the directory it might be slowish), but it's simple, easy to read and also has the advantage that regexes are more powerful than plain Bash glob patterns.