【问题标题】:Bash check shows file exists for non-existent files?Bash 检查显示文件存在不存在的文件?
【发布时间】:2015-03-27 14:55:53
【问题描述】:

在 bash 中运行以下命令:

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

(包中不存在文件,退出代码 = 1,stdout 为空)

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

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

file $stuff

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

stat $stuff

缺少操作数...

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

【问题讨论】:

    标签: bash


    【解决方案1】:

    您可能需要将$stuff 括在引号中

    if [ -f "$stuff" ]; then
    

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

    我发现将 shell 脚本中的变量或变量视为“宏”更有用,它们在首次使用时会扩展为它们的值。这与几乎所有其他编程语言中的变量都不同。

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

    [ -f hello world ]
    

    这显然是一个错误。

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

    [ -f ]
    

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

    1 个参数:
    如果 $1 不为空,则退出 true (0);否则,退出false。

    这大概是为了方便写:

    [ $variable_that_may_or_may_not_be_defined ]
    

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

    if [ -f "" ]; then
    

    【讨论】:

    • 我不敢相信我没有尝试过……我一定是笨笨笨笨的……谢谢!
    • 有人能解释一下为什么[ -ftest -f 在没有参数的情况下返回 0 吗?文档并没有真正提到这种情况。
    • [ string ] 被解释为[ -n string ][ -f ] 等价于 [ -n -f ],这始终是正确的:-f 不是空字符串。
    【解决方案2】:

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

    由于可能不明显是否以及何时选择[[ ... ]] 而不是[ ... ](及其(虚拟)别名test ...),让我尝试总结一下:

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

    请注意,kshzsh 也支持 [[ ... ]](可能在行为上有细微的变化)。

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


    [[ ... ]][ ... ] / test ... 的改进如下

    下面的“RHS”表示“右手边”,即二元运算符的右操作数。

    • (通常)不需要引用变量引用(除了在 ===~ 的 RHS 上指定 文字 字符串或子字符串)

      • f='some file'; [[ -f $f ]] # ok, double quotes optional
      • v='*'; [[ $v == '*' ]] # ok, double quotes optional
      • [[ ... ]] 内部既不应用分词也不应用路径名扩展,因此可以安全地使用不带引号的变量引用其值具有嵌入空格和/或值(例如 *)的变量,这通常会导致通配符。
    • 提供与= / == 匹配的字符串模式 匹配,并在RHS 上使用未引用 模式(或至少未引用模式元字符。)

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

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

    • [[ ... ]][ ... ],尽管这通常无关紧要。

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

    关于[test的实现说明

    • [[ a 是 shell keyword(在 bashkshzsh 中受支持),它允许使用不同的解析规则,如上所述。
    • 相比之下,[test 是所有主要的类似 POSIX 的 shell(bashkshzshdash)中的内置
    • 此外,[test 都作为外部实用程序存在(需要单独的进程调用的可执行文件) ,如mandated by POSIX
      • 事实上,您需要外部实用程序版本,以便能够在“无壳”调用场景中使用[test,例如将测试传递给find -execxargs
      • 虽然[ 实用程序可以被实现为test 实用程序的符号链接(只要test 知道它是如何被调用的并且在以[ 调用时强制关闭]),在实践它们通常(总是?)单独的可执行文件(例如,在 Linux 和 macOS / BSD 上是正确的;在 Linux 上,它们的内容不同,而在 macOS / BSD 上它们的内容是相同的(它们是同一文件的副本)。

    【讨论】:

      【解决方案3】:

      正如 Carpetsmoker 所说,一种选择是将 $stuff 放在引号中。

      但由于这是标记为 bash,并且因为在文件名中处理空格很痛苦,所以您可以选择:

      if [[ -f $stuff ]]
      

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

      【讨论】:

      • 很高兴知道,我使用 [[ 进行带有 != 和 == 的逻辑等价测试
      • @Nik:这是[[ 的附加功能之一:=~ 运算符用于将字符串与正则表达式进行匹配。
      猜你喜欢
      • 1970-01-01
      • 2014-10-30
      • 2012-05-26
      • 1970-01-01
      • 1970-01-01
      • 2023-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多