bash 链式逻辑运算符执行顺序,惰性求值

Zol*_* K. 6 bash

我不明白这个:

脚本: WORKDIR/sh/script.sh

[ -e filename ]          \
&& echo filename         \
|| [ -e ../filename ]    \
&& echo ../filename      \
|| { echo 'ERROR: failed to find "filename"' 1>&2 ; exit -1; }
Run Code Online (Sandbox Code Playgroud)

输出:

$ cd WORKDIR/sh
$ ./script.sh
../filename

$ cd WORKDIR
$ sh/script.sh
filename
../filename      # <---- WHY????
Run Code Online (Sandbox Code Playgroud)

我的想法:

1

[ -e filename ]          \ -> false
&&                         -> skip this, it is already false
   echo filename         \ -> don't even try
|| [ -e ../filename ]    \ -> true
&& echo ../filename      \ -> true
||                         -> already true, skip the rest
   { echo 'ERROR: failed to find "filename"' 1>&2 ; exit -1; }
Run Code Online (Sandbox Code Playgroud)

2

[ -e filename ]          \ -> true
&& echo filename         \ -> true
||                         -> already true, skip the rest
   [ -e ../filename ]    \
&& echo ../filename      \
|| { echo 'ERROR: failed to find "filename"' 1>&2 ; exit -1; }
Run Code Online (Sandbox Code Playgroud)

版本:

bash --version
GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
Run Code Online (Sandbox Code Playgroud)

jes*_*e_b 9

&&||具有相同的优先级,因此:

当一个命令通过时,它会寻找下一个&&并执行它,即使它不是直接相邻的操作符。您永远不应在单个命令列表中使用多个这些运算符。如果需要多个,则使用 if/then 构造。

$ true && true || echo yes && echo no
no
Run Code Online (Sandbox Code Playgroud)

这与以下非常不同:

if true; then 
  true 
else 
  echo yes && echo no
fi
Run Code Online (Sandbox Code Playgroud)
$ if true; then true; else echo yes && echo no; fi
$
Run Code Online (Sandbox Code Playgroud)

或者:

$ true && false || echo yes && echo no
yes
no
$ if true; then false; else echo yes && echo no; fi
$
Run Code Online (Sandbox Code Playgroud)

我会把你的构造写成:

if [ -e filename ]; then
    echo filename
elif [ -e ../filename ]; then
    echo ../filename
else
    echo 'ERROR: failed to find "filename"' >&2
    exit -1
fi
Run Code Online (Sandbox Code Playgroud)

  • 或者,更令人困惑的是,`true &amp;&amp; false || 回声是&amp;&amp;回声否`!(这说明了您的主要观点。) (2认同)
  • 对。我认为,如果先前运行的命令满足紧接在它之前的运算符所表达的条件,那么说每个命令都会运行更正确;参见例如`true &amp;&amp; false || echo yes &amp;&amp; false &amp;&amp; echo no`。如果第一个成功,则所有 `&amp;&amp;` 表达式都不会运行。 (2认同)

gle*_*man 6

我认为您希望 bash 这样做:

(A && B) || (C && D) || E
Run Code Online (Sandbox Code Playgroud)

但它实际上是这样做的

(A && B || C) && D || E
Run Code Online (Sandbox Code Playgroud)

其中d执行,如果任一B或C成功。

添加更多分组:

(A && B) || (C && D) || E
Run Code Online (Sandbox Code Playgroud)

或者

(A && B || C) && D || E
Run Code Online (Sandbox Code Playgroud)

或者使用if-elif-else@Jesse_b 展示的非常清晰易读的风格。


一步一步,这正在发生:

  1. 文件名在当前目录中:

    [ -e filename ]    \   # test succeeds, status is now 0
    &&                     # status is zero, will execute this branch
    echo filename      \   # echo succeeds, status is now 0
    ||                     # status is zero, do not execute
    [ -e ../filename ] \   # not executed
    &&                     # status is zero, will execute this branch
    echo ../filename   \   # echo succeeds, status is now 0
    ||                     # status is zero, do not execute
    { echo; exit; }        # not executed
    
    Run Code Online (Sandbox Code Playgroud)
  2. 文件名在父目录中:

    [ -e filename ]    \   # test fails, status is now 1
    &&                     # status is non-zero, do not execute this branch
    echo filename      \   # not executed
    ||                     # status is non-zero, will execute this branch
    [ -e ../filename ] \   # test succeeds, status is now 0
    &&                     # status is zero, will execute this branch
    echo ../filename   \   # echo succeeds, status is now 0
    ||                     # status is zero, do not execute
    { echo; exit; }        # not executed
    
    Run Code Online (Sandbox Code Playgroud)