if else语句是否等于逻辑和&&或|| 我应该在哪儿偏爱一个?


27

我正在学习决策结构,并且遇到了以下代码:

if [ -f ./myfile ]
then
     cat ./myfile
else
     cat /home/user/myfile
fi


[ -f ./myfile ] &&
cat ./myfile ||
cat /home/user/myfile

两者的行为相同。使用另一种方式有什么优势?



3
它们相等。参见伊卡洛斯的出色回答。例如,考虑存在./myfile但不可读的情况。
AlexP


Answers:


26

不,结构if A; then B; else C; fiA && B || C不等价的

使用时if A; then B; else C; fiA始终对命令进行评估和执行(至少尝试执行该命令),然后对命令B或命令C进行评估和执行。

A && B || C,这是相同的各种命令AB但对于不同C:命令C被评估并执行,如果任一 A发生故障 B失败。

在您的示例中,假设您chmod u-r ./myfile,尽管[ -f ./myfile ]成功,您仍会cat /home/user/myfile

我的建议:使用A && BA || B全部使用,这仍然易于阅读和理解,没有陷阱。但是,如果您的意思是如果...然后...其他...则使用if A; then B; else C; fi


29

大多数人觉得它更容易理解的if... ... then... ... else... ... fi形式。

对于a && b || c,您必须确保b返回true。这是导致细微错误的原因,也是避免这种样式的一个很好的理由。如果b不返回true,则它们不相同。

 $ if true; then false ; else echo boom ; fi
 $ true && false || echo boom
 boom

对于非常短的没有else子句的测试和操作,缩短的长度很有吸引力,例如

 die(){ printf "%s: %s\n" "$0" "$*" >&2 ; exit 1; }

 [ "$#" -eq 2] || die "Needs 2 arguments, input and output"

 if [ "$#" -ne 2 ] ; then
     die "Needs 2 arguments, input and output"
 fi

&&||short circuiting operators,只要已知结果,就会跳过进一步不需要的测试。a && b || c分组为(a && b) || c。首先a运行。如果将fails其定义为不返回退出状态0,则该组(a && b)是已知的fail并且b不需要运行。在||不知道表达式的结果所以需要执行c。如果a成功(返回零),则&&操作员尚不知道这样的结果,a && b因此必须运行b才能找出答案。如果b成功则a && b成功,并且||知道总体结果就是成功,因此不需要运行c。如果b失败的话||仍然不知道表达式的值,所以需要运行c


7

如果前一个命令执行成功,则运算符&&执行下一个命令(返回的退出代码($?)0 =逻辑为真)。

在形式中A && B || C,对命令(或条件)A进行求值,如果A返回true(成功,退出代码0),则执行命令B。如果失败(从而将返回 - 0以外的退出代码)和/或失败(返回),则命令Ç将被执行。

&&算作为AND状况检查和运营商||的作品像OR状况检查。

根据您想对脚本执行的操作,表单A && B || C可以像示例一样用于条件检查,也可以用于链接命令,并确保如果先前的命令成功退出代码为0,则可以执行一系列命令。
这就是为什么常见的命令如:的原因
do_something && do_something_else_that_depended_on_something

示例:
apt-get update && apt-get upgrade 如果更新失败,则不执行升级(在现实世界中是有道理的...)。

mkdir test && echo "Something" > test/file
echo "Something"仅当mkdir test成功且操作返回退出代码0时,才会执行 该部分。

./configure --prefix=/usr && make && sudo make install
通常在编译作业中找到,以将必要的依赖命令链接在一起。

如果您尝试使用if - then - else来实现上述“链”,那么您将需要更多的命令和检查(以及更多的代码编写-更多的错误地方)来完成一个简单的任务。

另外,请记住,用&&||链接的命令 由shell从左到右读取。您可能需要对命令和条件检查进行分组,以使下一步取决于某些先前命令的成功输出。例如查看此:

root@debian:$ true || true && false;echo $?
1 
#read from left to right
#true OR true=true AND false = false = exit code 1=not success

root@debian:$ true || (true && false);echo $?
0 
# true OR (true AND false)=true OR false = true = exit code 0 = success

或一个真实的例子:

root@debian:$ a=1;b=1;c=1;[[ $a -eq 1 ]] || [[ $b -eq 1 ]] && [[ $c -eq 2 ]];echo $?
1 
#condition $a = true OR condition b = true AND condition $c = false
#=> yields false as read from left to right, thus exit code=1 = not ok

root@debian:$ a=1;b=1;c=1;[[ $a -eq 1 ]] || [[ $b -eq 1 && $c -eq 2 ]];echo $?
0 
#vars b and c are checked in a group which returns false, 
#condition check of var a returns true, thus true OR false yields true = exit code 0

请记住,某些命令根据执行的进程返回不同的退出代码,或者根据其操作返回不同的代码(例如,命令GNU diff,如果两个文件不同,则返回1,否则,则返回0)。此类命令必须在&&||中小心对待。。

同样,为了解决所有难题,请注意使用;operator来连接命令。使用格式时A;B;C,无论命令A和的退出代码是什么,所有命令都会依次执行B


1

关于此的许多困惑可能是由于bash文档调用了这些AND和OR列表。虽然在逻辑上类似于和,&&||在方括号内找到,但它们的功能有所不同。

一些例子可以最好地说明这一点。

注意:单方括号和双方括号([ ... ][[ ... ]])本身就是执行比较并返回退出代码的命令。他们实际上并不需要if

cmda  && cmdb  || cmdc

如果cmda退出,cmdb则执行。
如果cmda退出,cmdb则不执行,而是执行cmdc

cmda; cmdb  && cmdc  || cmdd

如何cmda忽略出口。
如果cmdb退出true,cmdc则执行。
如果cmdb退出为false,cmdc则不执行,而是执行cmdd

cmda  && cmdb; cmdc

如果cmda退出,cmdb则执行,然后执行cmdc
如果cmda退出,cmdb则不执行,而是执行cmdc

??为什么要cmdc执行?
因为对于解释器来说,分号(;)和换行符意味着完全相同的东西。Bash认为那行代码是...

cmda  && cmdb
cmdc  

为了达到预期效果,我们必须将cmdb; cmdc花括号括起来以使它们成为复合命令(group command)。附加的终止分号只是{ ...; }语法的要求。所以我们得到...

cmda && { cmdb; cmdc; }
如果cmda退出,cmdb则执行,然后执行cmdc
如果cmda退出,则不执行cmdbcmdc不执行。
执行继续到下一行。

用法

有条件的命令列表对于尽快从函数中返回,从而避免解释和执行许多不必要的代码,最有用。但是,多重函数返回意味着必须对保持简短的函数保持执着,因此更容易确保覆盖所有可能的条件。

这是一些运行代码的示例...

fnInit () {
  :
  _fn="$1"
  ### fnInit "${FUNCNAME}" ...
  ### first argument MUST be name of the calling function
  #
  [[ "$2" == "--help-all" ]]  && { helpAll                      ; return 0; }
  ### pick from list of functions
  #
  [[ "$2" == "--note-all" ]]  && { noteAll                      ; return 0; }
  ### pick from notes in METAFILE
  #
  [[ "$2" == "--version"  ]]  && { versionShow "${_fn}" "${@:3}"; return 0; }
  #
  [[ "$2" == "--function" ]]  && {
    isFnLoaded "$3"           && { "${@:3}"                     ; return 0; }
    #
    errorShow functionnotfound "Unknown function:  $3"
    return 0
  }
  ### call any loaded function
  #
  [[ "$2" == "--help" || "$2" == "-h" ]]  && { noteShow "$_fn" "${@:3}"; return 0; }
  ### fnInit "${FUNCNAME}" --help or -h
  #
  return 1
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.