bash 的条件表达式中 -a 和 -e 的区别是什么?

pol*_*lym 12 shell bash test

来自man bash

CONDITIONAL EXPRESSIONS
[...]
       -a file
              True if file exists.
[...]
       -e file
              True if file exists.
Run Code Online (Sandbox Code Playgroud)
  1. 那么[ -a $FILE ]和之间有什么区别[ -e $FILE ],如果有的话?
  2. 如果没有真正的区别,为什么存在两个用于相同目的的标志?

cuo*_*glm 13

bash, 带有两个参数test命令的上下文,-a file并且-e file是相同的。但它们有一些区别,因为-a也是二元运算符。

-e一元是由 POSIX 定义的,但-a一元不是。POSIX 只定义-a二进制(见测试POSIX)。

POSIX 定义了三个参数test行为:

3个参数:

  • 如果 $2 是二进制主,则执行 $1 和 $3 的二进制测试。

  • 如果 $1 是 '!',则否定 $2 和 $3 的两个参数测试。

  • 如果 $1 是 '(' 并且 $3 是 ')',则执行 $2 的一元测试。在不支持 XSI 选项的系统上,如果 $1 是 '(' 并且 $3 是 ')',结果是未指定的。

  • 否则,会产生未指定的结果。

所以-a也会导致奇怪的结果:

$ [ ! -a . ] && echo true
true
Run Code Online (Sandbox Code Playgroud)

-a在三个参数的上下文中被视为二元运算符。请参阅Bash FAQ 问题 E1。POSIX 还提到它-a是从 KornShell 获取的,但后来更改为,-e因为它使-a二元和-a一元之间产生混淆。

添加了 -e 主函数,具有与 C shell 提供的功能类似的功能,因为它为 shell 脚本提供了无需尝试打开文件即可确定文件是否存在的唯一方法。由于允许实现添加其他文件类型,因此可移植脚本不能使用:

测试 -b foo -o -c foo -o -d foo -o -f foo -o -p foo

找出 foo 是否是现有文件。在历史 BSD 系统上,文件的存在可以通过以下方式确定:

测试 -f foo -o -d foo

但是没有简单的方法可以确定现有文件是常规文件。早期的提议使用了 KornShell -a primary(具有相同的含义),但后来改为 -e,因为人们担心人们很可能会将 -a primary 与 -a 二元运算符混淆。

-abinary 也被标记为过时,因为它会导致一些含糊不清的表达式,它有超过 4 个参数。有了这些 >4 个参数的表达式,POSIX 定义的结果是未指定的。


pol*_*lym 9

.1. 那么 [ -a $FILE ] 和 [ -e $FILE ] 之间有什么区别,如果有的话?

完全没有区别。

在线路505-507test.c的bash的版本4.2.45(1)-release

case 'a':           /* file exists in the file system? */
case 'e':
  return (sh_stat (arg, &stat_buf) == 0);
Run Code Online (Sandbox Code Playgroud)

这表明两个标志之间没有真正的区别。

.2. 如果没有真正的区别,为什么存在两个用于相同目的的标志?

参见gnouc的回答。

  • 另一方面,鉴于`-a`在bash中*也是*一个布尔运算符(和),添加这个完全多余的附加含义似乎很容易出错;我猜这与与其他一些实现的兼容性和/或与没有`-e` 的早期版本的兼容性有关。 (3认同)

jim*_*-cl 5

我找到的最好的答案是这个,来自 StackOverflow 问题:

-a已弃用,因此不再在联机帮助页中列出/usr/bin/test,但仍然在 bash的联机帮助页中 。使用-e. 对于单个 '[',bash 内置函数的行为与testbash 内置 函数的行为相同,后者的行为与/usr/bin/[和相同/usr/bin/test(一个是另一个的符号链接)。注意 的效果-a取决于它的位置:如果它在开头,则表示file exists。如果它在两个表达式的中间,则表示逻辑and

[ ! -a /path ] && echo exists不起作用,因为 bash 手册指出它在那里-a被认为是一个二元运算符,所以上面的不是解析为 anegate -a ..而是解析为 a if '!' and '/path' is true(非空)。因此,您的脚本总是输出"-a"(实际上是测试文件),而"! -a"这里实际上是二进制文件 and

对于[[,-a不再用作二进制文件and&&在那里使用),因此其唯一目的是检查那里的文件(尽管已弃用)。所以,否定实际上做了你所期望的。