我正在学习决策结构,并且遇到了这些代码:
if [ -f ./myfile ]
then
cat ./myfile
else
cat /home/user/myfile
fi
[ -f ./myfile ] &&
cat ./myfile ||
cat /home/user/myfile
他们俩的行为都是一样的。使用一种方式与另一种方式相比有什么优点吗?
答案1
大多数人发现更容易理解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
。
答案2
不,结构if A; then B; else C; fi
和A && B || C
是不等同。
使用 时if A; then B; else C; fi
,总是会评估并执行 command A
(至少会尝试执行它),然后评估并执行commandB
或 command 。C
对于命令来说A && B || C
,它是相同的A
,但B
对于命令来说是不同的:如果满足以下条件,则评估并执行C
命令C
任何一个 A
失败或者 B
失败。
在你的例子中,假设你chmod u-r ./myfile
,那么,尽管[ -f ./myfile ]
成功了,你会cat /home/user/myfile
我的建议:使用A && B
或使用A || B
您想要的所有内容,这仍然易于阅读和理解,并且没有陷阱。但如果你的意思是 if...then...else...then 使用if A; then B; else C; fi
.
答案3
操作员&&如果上一个命令成功执行,则执行下一个命令(返回退出代码 ($?) 0 = 逻辑 true)。
形式A && B || C
、命令(或条件)A被评估并且如果A回报真的(成功,退出代码0)然后命令乙被执行。如果A失败(因此将返回错误的- 除 0 之外的退出代码)和/或乙失败(返回错误的)然后命令C将被执行。
运算符也&&
用作和在条件检查和操作员的||
工作原理如下或者在状况检查中。
根据您想要对脚本执行的操作,表单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
通常在编译作业时发现,将必要的依赖命令链接在一起。
如果您尝试使用以上“链”来实现如果-然后-别的对于一个简单的任务,您将需要更多的命令和检查(因此需要编写更多的代码 - 更多的事情会出错)。
另外,请记住,链式命令&&和||由 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如果他们不这样做)。此类命令需要小心对待&&和||。
另外,为了将所有难题放在一起,请注意使用运算符连接命令;
。使用格式时,无论 command和A;B;C
的退出代码是什么,所有命令都将连续执行。A
B
答案4
对此的大部分困惑可能是由于 bash 文档调用了这些AND 和 OR 列表。虽然在逻辑上与方括号内的&&
和类似||
,但它们的功能不同。
一些例子可以最好地说明这一点......
注意:单方括号和双方括号(
[ ... ]
和[[ ... ]]
)本身就是执行比较并返回退出代码的命令。他们实际上并不需要if
.
cmda && cmdb || cmdc
如果cmda
exit true,cmdb
则执行。
如果cmda
退出为 false,cmdb
则不会执行,但cmdc
会执行。
cmda; cmdb && cmdc || cmdd
如何cmda
退出被忽略。
如果cmdb
exit true,cmdc
则执行。
如果cmdb
退出为 false,cmdc
则不执行,而是cmdd
执行。
cmda && cmdb; cmdc
如果cmda
exit true,cmdb
则执行,然后执行cmdc
。
如果cmda
退出为 false,cmdb
则不执行但是cmdc
。
啊?为何被cmdc
处决?
因为对于解释器来说,分号( ;
)和换行符的含义完全相同。 Bash 将该行代码视为...
cmda && cmdb
cmdc
为了达到预期的效果,我们必须将它们括cmdb; cmdc
在花括号内,使它们成为复合命令(组命令)。附加的终止分号只是{ ...; }
语法的要求。所以我们得到...
cmda && { cmdb; cmdc; }
如果cmda
exit true,cmdb
则执行,然后执行cmdc
。
如果cmda
退出为 false,则cmdb
或都不cmdc
被执行。
执行继续到下一行。
用法
条件命令列表对于尽快从函数返回最有用,从而避免解释和执行大量不必要的代码。然而,多个函数返回意味着,必须致力于保持函数简短,以便更容易确保涵盖所有可能的条件。
这是一些运行代码的示例......
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
}