Bash 否定 -a(文件存在)不会改变结果,而 for ! -e 改变结果

Bash 否定 -a(文件存在)不会改变结果,而 for ! -e 改变结果

我很困惑,但仍然猜想我以某种方式误解了 Bash。

/$ if [   -e /bin/grep ]; then echo yea; else echo nay ; fi
yea
/$ if [ ! -e /bin/grep ]; then echo yea; else echo nay ; fi
nay
/$ if [   -a /bin/grep ]; then echo yea; else echo nay ; fi
yea
/$ if [ ! -a /bin/grep ]; then echo yea; else echo nay ; fi
yea

为什么否定!会逆转测试的效果-e而不是-a测试?

男人巴什 说:

测试

3 个参数

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

  1. 如果第二个参数是上面条件表达式下列出的二进制条件运算符之一,则表达式的结果是使用第一个和第三个参数作为操作数的二进制测试的结果。当存在三个参数时,-aand运算符被视为二元运算符。-o
  2. 如果第一个参数是!,则该值是使用第二个和第三个参数进行双参数测试的否定。

Bash 条件表达式

[[ 条件表达式由复合命令以及test[内置命令使用

-a file
如果文件存在则为真。
-b file
如果文件存在并且是块特殊文件,则为 True。
-c file
如果文件存在并且是字符特殊文件,则为 True。
-d file
如果文件存在并且是目录,则为 True。
-e file
如果文件存在则为真。

答案1

-a既是一元运算符(对于a可访问性,添加是为了与 Korn shell 兼容,但在其他方面是非标准的,现在与 冗余-e)和二进制运算符(对于and,在 POSIX(使用 XSI)中,但在那里已弃用)运算符。

这里[ ! -a /bin/grep ]调用了二进制POSIX 要求的运算符。是[ "$a" -a "$b" ]为了测试是否$a非空 $b非空,这里有$a==!$b== /bin/grep。由于两个字符串都非空,因此返回真的

另请参阅“当存在三个参数时,-a 和 -o 运算符被视为二元运算符”在你引用的文字中。

-a一元和二进制形式均已弃用,一元形式是因为它被 取代-e,二进制形式是因为它会导致不可靠且不明确的测试表达式。

测试文件是否存在(尽管实际上,它更多的是测试文件是否存在)无障碍,是否stat()会在路径上成功 1),使用[ -e filepath ].到两个条件,&&在两次调用之间使用[

为了测试字符串是否非空,我个人更喜欢表单[ -n "$string" ]而不是[ "$string" ]表单。

所以:

  • 测试文件是否存在:

    [ -e "$file" ]   # not [ -a "$file" ]
    [ ! -e "$file" ] # not [ ! -a "$file" ]
    
  • 测试两个字符串是否非空:

    [ -n "$a" ] && [ -n "$b" ] # not [ "$a" -a "$b" ]
    [ "$a" ] && [ "$b" ]
    

POSIX 规范中test(aka [) 实用程序的基本原理

指定 -a 和 -o 二进制原色以及“(”和“)”运算符的 XSI 扩展已被标记为过时。 (许多使用它们的表达式是由语法模糊定义的,具体取决于正在评估的特定表达式。)使用这些表达式的脚本应转换为下面给出的形式。尽管许多实现将继续支持这些过时的形式,但脚本在处理用户提供的输入时应该非常小心,因为这些输入可能会与这些和其他原色和运算符混淆。

和:

早期的提案使用了 KornShell -a 主运算符(具有相同的含义),但后来将其更改为 -e,因为人们担心人们很可能将 -a 主运算符与 -a 二元运算符混淆。

yashbosh、 GNU的手册coreutils确实防止在各自的实现中使用二进制-a/ ,并且手册从未记录过它们²,但不幸的是,包括(GNU shell)在内的许多其他手册仍然不阻止它们的使用,也不反对它们。-o[testzshbash


1 更多相关内容请参见这是我对相关 stackoverflow 问答的回答

² a test/[内置函数仅在 1991 年的 2.0.3 版本中添加到 zsh。[[ ... ]]来自 Korn shell 的特殊构造始终是首选,并且具有自己的语法,其中&&||用于或者运营商。

答案2

Bash 解析[ ! -a WORD ]-a二元运算符,意思是“and”,周围有两个单字测试,如果非空则为 true。因此[ ! -a "" ]为假,如果非空[ ! -a WORD ]则为真。WORD

这个解析符合POSIX规范(尽管严格来说,不必这样做,因为-aPOSIX 未指定一元)。顺便说一句, mksh 和 zsh 以相同的方式解析这种情况,但 ksh93 使用 unary!后跟 unary 来解析它-a

为了避免歧义,请勿使用已弃用的运算-a符,而应使用标准-e运算符,该运算符没有拼写相同的二元运算符。此外,为了避免较长极端情况下的歧义或错误,请使用&&and||作为二元运算符(外部[ … ]或内部[[ … ]]),而不是可能存在歧义的-a/ -oinside [ … ]

相关内容