bash Bash使用双括号测试和通配符检查文件是否存在
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/24615535/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Bash check if file exists with double bracket test and wildcards
提问by William Everett
I am writing a Bash script and need to check to see if a file exists that looks like *.$1.*.ext
I can do this really easily with POSIX test as [ -f *.$1.*.ext ]
returns true, but using the double bracket [[ -f *.$1.*.ext ]]
fails.
我正在编写一个 Bash 脚本,需要检查是否存在一个文件,看起来*.$1.*.ext
我可以很容易地使用 POSIX 测试来完成此操作,因为[ -f *.$1.*.ext ]
返回 true,但使用双括号[[ -f *.$1.*.ext ]]
失败。
This is just to satisfy curiosity as I can't believe the extended testing just can't pick out whether the file exists. I know that I can use [[ `ls *.$1.*.ext` ]]
but that will match if there's more than one match. I could probably pipe it to wc
or something but that seems clunky.
这只是为了满足好奇心,因为我无法相信扩展测试无法确定文件是否存在。我知道我可以使用,[[ `ls *.$1.*.ext` ]]
但如果有多个匹配项,它将匹配。我可能可以通过管道将它传输到wc
其他东西,但这看起来很笨重。
Is there a simple way to use double brackets to check for the existence of a file using wildcards?
有没有一种简单的方法可以使用双括号来检查使用通配符的文件是否存在?
EDIT: I see that [[ -f `ls -U *.$1.*.ext` ]]
works, but I'd still prefer to not have to call ls.
编辑:我认为这[[ -f `ls -U *.$1.*.ext` ]]
可行,但我仍然希望不必调用 ls。
回答by mklement0
Neither [ -f ... ]
nor [[ -f ... ]]
(nor other file-test operators) are designed to work with patterns(a.k.a. globs, wildcard expressions)- they always interpret their operand as a literalfilename.[1]
也不[ -f ... ]
是[[ -f ... ]]
(也不是其他文件测试运算符)都设计为与模式(又名通配符,通配符表达式)一起使用- 它们总是将其操作数解释为文字文件名。 [1]
A simple trick to test if a pattern (glob) matches exactly onefileis to use a helper function:
测试模式(glob)是否与一个文件完全匹配的一个简单技巧是使用辅助函数:
existsExactlyOne() { [[ $# -eq 1 && -f ]]; }
if existsExactlyOne *."".*.ext; then # ....
If you're just interested in whether there are anymatches - i.e., one or more- the function is even simpler:
如果您只对是否有任何匹配项感兴趣- 即,一个或多个- 该函数甚至更简单:
exists() { [[ -f ]]; }
If you want to avoid a function, it gets trickier:
如果你想避免一个函数,它会变得更棘手:
Caveat: This solution does not distinguish between regular files directories, for instance (though that could be fixed.)
警告:例如,此解决方案不区分常规文件目录(尽管可以修复。)
if [[ $(shopt -s nullglob; set -- *."".*.ext; echo $#) -eq 1 ]]; then # ...
- The code inside the command substitution (
$(...)
) does the following:shopt -s nullglob
instructs bash to expand the pattern to an emptystring, if there are nomatchesset -- ...
assigns the results of the pattern expansion to the positional parameters ($1
,$2
, ...) of the subshell in which the command substitution runs.echo $#
simply echoes the count of positional parameters, which then corresponds to the count of matching files;
- That echoed number (the command substitution's stdout output) becomes the left-hand side to the
-eq
operator, which (numerically) compares it to1
.
- 命令替换 (
$(...)
) 中的代码执行以下操作:shopt -s nullglob
如果没有匹配项,则指示 bash 将模式扩展为空字符串set -- ...
将模式扩展的结果分配给运行命令替换的子 shell的位置参数 ($1
,$2
, ...)。echo $#
简单地回显位置参数的计数,然后对应于匹配文件的计数;
- 该回显数字(命令替换的标准输出输出)成为
-eq
运算符的左侧,它(数字上)将其与1
.
Again, if you're just interested in whether there are anymatches - i.e., one or more- simply replace -eq
with -ge
.
同样,如果您只对是否有任何匹配项感兴趣- 即,一个或多个- 只需替换-eq
为-ge
.
[1]
As @Etan Reisinger points out in a comment, in the case of the [ ... ]
(single-bracket syntax), the shell expands the pattern beforethe -f
operator even sees it (normal command-line parsing rules apply).
[1]
正如@Etan Reisinger 在评论中指出的那样,在[ ... ]
(单括号语法)的情况下,shell 会在操作员看到之前扩展模式-f
(适用正常的命令行解析规则)。
By contrast, different rules apply to bash's [[ ... ]]
, which is parsed differently, and in this case simply treats the pattern as a literal(i.e., doesn't expand it).
相比之下,不同的规则适用于 bash 的[[ ... ]]
,它的解析方式不同,在这种情况下,简单地将模式视为文字(即,不扩展它)。
Either way, it won't work (robustly and predictably) with patterns:
无论哪种方式,它都不会(稳健且可预测地)与模式一起工作:
- With
[[ ... ]]
it neverworks: the pattern is always seen as a literal by the file-test operator. - With
[ ... ]
it onlyworks properly if there happens to be exactly ONE match.- If there's NO match:
- The file-test operator sees the pattern as a literal, if
nullglob
is OFF (the default), or, ifnullglob
is ON, the conditional always returns true, because it is reduced to-f
, which, due to the missing operand, is no longer interpreted as a file test, but as a nonempty string(and a nonempty string evaluates to true)).
- The file-test operator sees the pattern as a literal, if
- If there are MULTIPLE matches: the
[ ... ]
command breaks as a whole, because the pattern then expands to multiple words, whereas file-test operators only take oneargument.
- If there's NO match:
- 有了
[[ ... ]]
它从来没有工作原理:该模式一直被视为一个文字与文件测试操作。 - 有了
[ ... ]
它只是正常工作,如果有恰好是只有一个匹配。- 如果没有匹配项:
- 文件测试运算符将模式视为文字,如果
nullglob
为 OFF(默认值),或者如果nullglob
为 ON,则条件总是返回 true,因为它被简化为-f
,由于缺少操作数,不再解释作为文件测试,但作为非空字符串(并且非空字符串评估为真))。
- 文件测试运算符将模式视为文字,如果
- 如果有多个匹配项:
[ ... ]
命令作为一个整体中断,因为模式随后扩展为多个单词,而文件测试运算符仅采用一个参数。
- 如果没有匹配项:
回答by Deleted User
as your question is bash tagged, you can take advantage of bash specific facilities, such as an array:
由于您的问题是 bash 标记的,您可以利用 bash 特定工具,例如数组:
file=(*.ext)
[[ -f "$file" ]] && echo "yes, ${#file[@]} matching files"
this first populates an array with one item for each matching file name, then tests the first item only: Referring to the array by name without specifying an index addresses its first element. As this represents only one single file, -f behaves nicely.
这首先为每个匹配的文件名填充一个项目,然后仅测试第一项:按名称引用数组而不指定索引地址其第一个元素。由于这仅代表一个文件,因此 -f 表现良好。
An added bonus is that the number of populated array items corresponds with the number of matching files, should you need the file count, and can thereby be determined easily, as shown in the echoed output above. You may find it an advantage that no extra function needs to be defined.
一个额外的好处是填充的数组项的数量与匹配文件的数量相对应,如果您需要文件计数,并且可以很容易地确定,如上面的回显输出所示。您可能会发现不需要定义额外的功能是一个优势。