Bash检查显示文件是否存在不存在的文件?
在bash中运行以下内容:
stuff=`rpm -ql <some package> | grep dasdasdfd`
(不存在的文件包,退出码= 1,标准输出为空)
if [ -f $stuff ]; then echo "whaaat"; fi
上面的命令检查文件是否存在…但是:
file $stuff
只需打印文件…的使用信息
stat $stuff
缺less操作数…
有人可以解释为什么吗? 这是一个错误? 我做错了什么? 我只想确保包中的文件存在于fs中
你可能需要在引号中包含$stuff
if [ -f "$stuff" ]; then
作为一般规则,您几乎总是希望在所有使用path名的地方添加引号。
我觉得将shell脚本中的variables或想法变成“macros”更加有用,它们在首次使用时被扩展为它们的值。 这与几乎所有其他编程语言中的variables都不同。
所以如果$stuff
包含hello world
(注意空间),这将是相同的,如果你已经键入:
[ -f hello world ]
这显然是一个错误。
在这种情况下,你提到你正在处理一个不存在的文件,所以$stuff
实际上是空的,这就像input:
[ -f ]
这实际上是有效的,但总是成功的。 这是一个晦涩难懂的test
行为,从POSIX规范我们读到,如果只有一个参数(在这种情况下,参数是-f
),那么test
总是成功的:
1个参数:
如果$ 1不为null,则退出true(0); 否则,退出false。
这可能有助于写作:
[ $variable_that_may_or_may_not_be_defined ]
如果您添加引号,则会传递2个参数,并且会发生更多理智的事情:
if [ -f "" ]; then
如Carpetsmoker所说,一种select是把$stuff
放在引号中。
但是,因为这是标签bash,并且因为迎合文件名空白是一个痛苦,你可以去:
if [[ -f $stuff ]]
与[
这是test
的别名]相反, [[
construct]“知道”如何正确处理$stuff
的内容。
Carpetsmoker的答案和DevSolar的答案都提供了正确的解决scheme和有用的背景信息:在一个案例中, [ ... ]
在另一个案例中。
因为如果和何时select[[ ... ]]
及其(虚拟)别名, test ...
),可能并不明显,请让我尝试一下总结:
- 如果您的代码必须是可移植的 (符合POSIX) ,您必须使用(或
test ...
) 。- 除非您明确希望所有的shell扩展 – 特别是分词(通过空格自动分割成多个标记)和通配符(globbing),否则就像在传递给可执行文件的参数一样 ,适用于他们。
-
[ -f "$stuff" ] # double-quoting required, if $stuff has embedded whitespace
- 如果您知道您的代码将以
bash
运行,您可以使用[[ ... ]]
获取更多function,减less意外 。-
[[ ... ]]
内的令牌在一个特殊的上下文中被parsing,在这个特殊的上下文中, 既不应用分词也不应该使用path名扩展(globbing) (尽pipe其他的扩展,例如参数扩展也会发生),所以通常不需要加倍- 引用variables引用 。 -
[[ -f $stuff ]] # double-quoting optional
-
请注意, ksh
和zsh
也支持[[ ... ]]
(假设行为有细微的变化)。
有关更多背景信息,请参阅[[ ... ]]
提供的附加function。
[[ ... ]]
改进如下 :
下面的“RHS”表示“右侧”,即二元运算符的右操作数。
-
(通常)不需要引用variables引用 (除了
==
和=~
的RHS以指定文字string或子string)-
f='some file'; [[ -f $f ]] # ok, double quotes optional
-
v='*'; [[ $v == '*' ]] # ok, double quotes optional
- 在
[[ ... ]]
内部不应用单词拆分或path名扩展,因此可以安全地使用未加引号的引用来引用其值已embedded空格和/或值(例如*
variables,这些variables通常会导致通配符。
-
-
提供与
=
/==
匹配的string模式 ,在RHS(或至less不带引号的模式元字符)上带有一个不带引号的模式。-
[[ abc == a* ]] && echo yes # matches; use of = instead of == works too
- 警告 :因此,在
=
/==
的RHS上,如果您希望将其值作为文字对待,则必须双引号引用variables(或单引号文字)。-
v='a*'; [[ abc == "$v" ]] # does NOT match
-
-
- 提供与
=~
匹配的正则expression式,在RHS(或至less不带引号的正则expression式)上带有一个没有引号的正则expression式。-
[[ abc = ^a.+$ ]] && echo yes # matches
- 注意 :因此,如果您希望将其值作为文字对待,则在
=~
的RHS上, 必须重复引用variables引用(或单引号文字)。-
v='a.+'; [[ abc =~ ^"$v"$ ]] # does NOT match
- 另外请注意,引号/引号的区别仅在bash 3.2中引入 – 您仍然可以使用
shopt -s compat31
将单引号和双引号string视为正则expression式。
-
- 警告 :由
=~
理解的正则expression式是特定于平台的 ,所以在一个平台上工作的正则expression式可能不适用于另一个平台(这是bash行为依赖于平台的less数情况之一)。 例如,在Linux上,你可以使用\b
和\<
/\>
来进行字边界断言,而BSD / OSX只支持[[:<]]
和[[:>]]
, t支持 – 请参阅https://stackoverflow.com/a/12696899/45375 。
-
- 提供分组和否定与
(
,)
和!
字符 。 - 使用
&&
和||
(布尔AND和OR)-
[[ (3 -gt 2) && ! -f / ]] && echo yes
- 请注意,在
[[ ... ]]
,&&
优先级高于||
– 与OUTSIDE(不同于所谓的[command-]列表操作符,它们将整个命令/命令列表结合在一起)不同,它们具有相同的优先级。 - (虽然
[
和test
有-a
和-o
,即使是POSIX规范,以防test
注意事项,请参阅http://man.cx/test )。
-
-
[[ ... ]]
比[ ... ]
快 ,但这通常不重要。- 如果您对相对性能感兴趣,请参阅https://stackoverflow.com/a/24194257/45375 。
执行说明重新[
和test
:
-
[[
a是shell 关键字 (在bash
,ksh
和zsh
),允许使用不同的分析规则,如上所述。 - 相比之下,
[
和test
内置在所有主要POSIX类似的shell(bash
,ksh
,zsh
,dash
)中。 - 另外,正如POSIX所要求的 ,
[
和test
都以外部实用程序 (需要单独进程调用的可执行文件)的forms存在。- 事实上,您需要外部实用程序版本,以便能够使用
[
或在“无shell”调用场景中testing,例如在通过testing来find -exec
或xargs
。 - 虽然
[
实用程序可以被想象成是作为符号连接到test
实用程序(只要test
知道如何被调用并强制closures),当调用为[
)时,实际上它们通常(总是)单独的可执行文件(true例如Linux和OSX / BSD;在Linux上,它们的内容不同,而在OSX / BSD上它们的内容是相同的(它们是相同文件的副本))。
- 事实上,您需要外部实用程序版本,以便能够使用