Bash检查显示不存在的文件存在文件?

时间:2015-03-27 14:55:53

标签: bash

在bash中运行以下命令:

   stuff=`rpm -ql <some package> | grep dasdasdfd`

(包中不存在的文件,退出代码= 1,标准输出为空)

  if [ -f $stuff ]; then echo "whaaat"; fi

上面的命令检查文件是否存在......但是:

file $stuff

只需打印文件的使用信息...和

stat $stuff

缺少操作数......

有人可以解释一下原因吗?这是一个错误吗?难道我做错了什么?我只是想确保包中的文件出现在fs

3 个答案:

答案 0 :(得分:6)

您可能需要在引号中加注$stuff

if [ -f "$stuff" ]; then

作为一般规则,您几乎始终希望在您使用它们的任何地方的路径名周围添加引号。

我觉得在shell脚本中考虑或变量更有用的是&#34;宏&#34;,它们在首次使用时会扩展为它们的值。这与几乎所有其他编程语言中的变量不同。

因此,如果$stuff包含hello world(请注意空格),则与您输入的内容相同:

[ -f hello world ]

这显然是一个错误。

在这种情况下,您提到您正在处理不存在的文件,因此$stuff实际上是空的,这就像打字:

[ -f ]

哪个实际上有效,但总是成功。这是一个模糊的test行为,从POSIX spec我们读到test总是成功,如果只有一个参数(在这种情况下,参数是-f ):

  

1个论点:
  如果$ 1不为null,则退出true(0);否则,退出false。

这可能是为了方便撰写:

[ $variable_that_may_or_may_not_be_defined ]

如果你添加引号,你就会传递2个参数,并且会发生更加明智的事情:

if [ -f "" ]; then

答案 1 :(得分:3)

Carpetsmoker说,其中一个选择就是将$stuff放在引号中。

但是因为这是标记为bash,并且因为在文件名中考虑空白是一种痛苦,所以你可以选择:

if [[ -f $stuff ]]

[的别名test相对,[[构造“知道”如何正确处理$stuff的内容。

答案 2 :(得分:3)

Martin Tournoij's answerDevSolar's answer都提供了正确的解决方案和有用的背景信息:在一种情况下为[ ... ],在另一种情况下为[[ ... ]]

由于选择[[ ... ]]超过[ ... ] (及其(虚拟)别名test ...)可能不明显,让我试一试摘要:

  • 如果代码必须可移植(符合POSIX标准),则必须使用[ ... ](或test ...
    • [ ... ]内的标记解析就像传递给可执行文件的参数一样,所以必须双引号变量引用,除非您明确要求所有shell扩展 - 特别是单词拆分(通过空格自动拆分成多个令牌)和通配 - 应用于它们。
    • [ -f "$stuff" ] # double-quoting required, if $stuff has embedded whitespace
  • 如果您知道代码将使用bash 运行,则可以使用[[ ... ]] 获取更多功能,减少意外情况即可。
    • [[ ... ]]内的标记在特殊上下文中解析,其中既不应用分词也不应用路径名扩展(globbing)(尽管其他扩展,例如参数扩展,发生),因此通常不需要双引号变量引用
    • [[ -f $stuff ]] # double-quoting optional

请注意,kshzsh也支持[[ ... ]](可能是行为的微妙变化)。

有关更多背景信息,例如[[ ... ]]提供的其他功能,请继续阅读。


[[ ... ]]改进[ ... ] / test ...,如下所示

&#34; RHS&#34;下面表示&#34;右侧&#34;,即二元运算符的右操作数。

  • (通常)不需要引用变量引用===~的RHS除外,以指定文字 string或substring(s))

    • f='some file'; [[ -f $f ]] # ok, double quotes optional
    • v='*'; [[ $v == '*' ]] # ok, double quotes optional
    • [[ ... ]]内部既不应用单词拆分也不应用路径名扩展,因此可以安全地使用对其值已嵌入空格和/或值通常为*的变量的非加引号引用导致全球化。
  • 提供字符串模式匹配= / ==,其中未加引号的模式RHS(或至少是未引用的模式元素)。

    • [[ abc == a* ]] && echo yes # matches; use of = instead of == works too
    • 警告:因此,在= / ==的RHS上,您必须双引号变量引用(或单引号文字)如果您希望将其值视为文字
      • v='a*'; [[ abc == "$v" ]] # does NOT match
  • 提供与=~ 匹配的正则表达式,在RHS上使用未加引号扩展正则表达式(或至少不带引号的正则表达式元数据)。
    • [[ abc =~ ^a.+$ ]] && echo yes # matches
    • 警告:因此,在=~的RHS中,如果您希望其值为,则必须双引号变量引用(或单引号文字)被视为文字
      • v='a.+'; [[ abc =~ ^"$v"$ ]] # does NOT match
      • 另请注意,未引用/引用的区别仅在bash 3.2中引入 - 您仍然可以使用shopt -s compat31将单引号和双引号字符串视为正则表达式。
    • 警告=~理解的正则表达式方言是特定于平台的,因此在一个平台上运行的正则表达式可能不适用于另一个平台(这是bash行为依赖于平台的少数情况之一。例如,在Linux上,您可以使用\b\< / \>进行字边界断言,而BSD / macOS仅支持[[:<]][[:>]],反过来,Linux并不支持 - 请参阅我的this answer
  • 使用 未转义 ()!字符提供分组和否定。
  • 提供使用&&|| (布尔AND和OR)

    • [[ (3 -gt 2) && ! -f / ]] && echo yes
    • 请注意,在[[ ... ]]内,&&更高优先级高于|| - 与OUTSIDE不同(如所谓的[command-]列表运算符,其中它们组合了整个命令/命令列表),它们具有相等的优先级。
    • [test-a-oeven the POSIX spec. for test cautions against their use
  • [[ ... ]]内,您可以在多行中传播条件以提高可读性,而无需使用行继续符号。 (\),假设换行符在&&|| 之后codeforester指出。

  • [[ ... ]] 更快 [ ... ]更快,但这通常无关紧要。

    • 如果您对相对表现感兴趣,请参阅我的this answer

实施说明重新[test

  • [[ a是shell 关键字bashkshzsh支持),它允许不同的解析规则,如如上所述。
  • 相比之下,[test在所有类似POSIX的主要shell中都是 builtins bashksh,{{1} },zsh)。
  • 此外,dash[都作为外部实用程序(可执行文件存在,需要单独的进程调用),mandated by POSIX
    • 事实上,您需要外部实用程序版本,以便能够在&#34; shell-less&#34;中使用test[调用方案,例如将测试传递给testfind -exec
    • 可以想象xargs实用程序可以实现为[实用程序的符号链接(只要test知道如何调用它,并强制关闭test时调用为]),实际上它们通常(总是?)单独的可执行文件(例如,在Linux和macOS / BSD上都是如此;在Linux上,它们的内容不同,而在macOS / BSD上它们的内容是相同的(它们是是同一文件的副本))。