测试! -a总是成功,无论文件是否存在

时间:2015-11-06 03:37:22

标签: bash

我得到以下输出:

$ touch bar
$ if [ -a bar ]; then echo "Yes"; fi
Yes
$ if [ ! -a bar ]; then echo "Yes"; fi
Yes
$ rm bar
$ if [ -a bar ]; then echo "Yes"; fi
$ if [ ! -a bar ]; then echo "Yes"; fi
Yes

我预计第二个命令不应该打印"是"因为! -a应与-a相反。出了什么问题?

使用-f代替-a的行为与我预期的一样。

版本为GNU bash, version 4.3.42(3)-release (i686-pc-cygwin)

3 个答案:

答案 0 :(得分:3)

来自bash的{​​{1}}手册页有三个参数(请注意我的重点):

  

以列出的顺序 应用以下条件。

     
      
  • 如果第二个参数是上面在CONDITIONAL EXPRESSIONS下列出的二元条件运算符之一,则表达式的结果是使用第一个和第三个参数作为操作数的二进制测试的结果。 当有三个参数时,['-a'运算符被视为二元运算符。

  •   
  • 如果第一个参数是'-o',则该值是使用第二个和第三个参数的双参数测试的否定。

  •   
  • 如果第一个参数正好是'!'且第三个参数正好是'(',那么结果就是第二个参数的单参数测试。

  •   
  • 否则,表达式为false。

  •   

换句话说,在这种情况下,')'的行为并不像您期望的那样。它实际上被视为逻辑和运算符,因为-a!都被认为是真的,bar它们的结果也是如此:

and

pax> if [ ! ] ; then echo yes ; fi yes pax> if [ bar ] ; then echo yes ; fi yes 的弱点很多而且多种多样,这就是明智[编码员会使用bash代替的原因: - )

答案 1 :(得分:2)

如果您想坚持使用[ ... ],请使用(引用)括号来消除歧义 [1]

if [ ! \( -a bar \) ]; then echo "Yes"; fi    

如果您正在编写bash脚本(无需担心可移植性),请使用[[ ... ]],这样就无需使用括号:

if [[ ! -a bar ]]; then echo "Yes"; fi       

[1] Barmar,在已删除的答案中,解释了[ ! -a bar ]中固有的歧义
-a两者作为一元文件存在运算符作为二进制逻辑AND运算符。
在目前的情况下,-a由于存在 3 参数,被解释为逻辑运算符,处理!和{{ 1}}作为 literal 操作数:bar!都是非空字符串,在布尔上下文中被认为是真实的,所以整体表达式总是为真。

相比之下,bar的{​​{1}}构造完全使用bash作为逻辑AND(并[[ ... ]]作为OR)以避免这种歧义;换句话说:&& 文件存在运算符,因此不存在歧义,并且不需要括号。

答案 2 :(得分:1)

试试这个:

$ touch bar
$ if [[ -a bar ]]; then echo "Yes"; fi
Yes
$ if [[ ! -a bar ]]; then echo "Yes"; fi
<NOTHING>

参考:http://mywiki.wooledge.org/BashFAQ/031

特殊原语[[被定义为,但[可能缺乏(取决于实施):

Description
Primitive
Example
entry (file or directory) exists
-e
[[ -e $config ]] && echo "config file exists: $config"
file is newer/older than other file
-nt / -ot
[[ $file0 -nt $file1 ]] && echo "$file0 is newer than $file1"
two files are the same
-ef
[[ $input -ef $output ]] && { echo "will not overwrite input file: $input"; exit 1; } 
negation
!
[[ ! -u $file ]] && echo "$file is not a setuid file"