如何声明和使用shell脚本中的布尔variables?
我尝试使用以下语法在shell脚本中声明一个布尔variables:
variable=$false variable=$true
它是否正确? 另外,如果我想更新该variables,我会使用相同的语法? 最后,使用布尔variables作为正确expression式的语法如下:
if [ $variable ] if [ !$variable ]
修订答案(2014年2月12日)
the_world_is_flat=true # ...do something interesting... if [ "$the_world_is_flat" = true ] ; then echo 'Be careful not to fall off!' fi
原始答复
注意事项: https : //stackoverflow.com/a/21210966/89391
the_world_is_flat=true # ...do something interesting... if $the_world_is_flat ; then echo 'Be careful not to fall off!' fi
From: 在Bash中使用布尔variables
这里包括原始答案的原因是因为2014年2月12日修订前的评论仅与原始答案有关,许多评论在与修订后的答案相关时是错误的。 例如,丹尼斯·威廉姆森在2010年6月2日发表的关于bash builtin的评论仅适用于原始答案,而不是修订版。
TL; DR
bool=true if [ "$bool" = true ]
与美九( 原始 )答案有关的问题
我不推荐接受的答案1 。 它的语法很漂亮,但有一些缺陷。
假设我们有以下的条件。
if $var; then echo 'Muahahaha!' fi
在以下情况2中 ,此条件将评估为true并执行嵌套命令。
# Variable var not defined beforehand. Case 1 var='' # Equivalent to var="". Case 2 var= # Case 3 unset var # Case 4 var='<some valid command>' # Case 5
通常情况下,只有当您的“布尔”variablesvar
在此示例中显式设置为true时,才会将条件评估为true。 所有其他案件都是危险的误导!
最后一种情况(#5)特别调皮,因为它会执行包含在variables中的命令(这就是为什么对于有效命令3,4,条件评估为真的原因)。
这是一个无害的例子:
var='echo this text will be displayed when the condition is evaluated' if $var; then echo 'Muahahaha!' fi # Outputs: # this text will be displayed when the condition is evaluated # Muahahaha!
引用你的variables是更安全的,例如, if "$var"; then
if "$var"; then
。 在上述情况下,您应该会收到警告,指出找不到该命令。 但是我们仍然可以做得更好(参见我的build议)。
另请参阅麦克·霍尔特(Mike Holt)对美九最初答案的解释。
问题与Hbar的答案
这种方法也有意想不到的行为。
var=false if [ $var ]; then echo "This won't print, var is false!" fi # Outputs: # This won't print, var is false!
您会期望上述条件评估为false,因此从不执行嵌套语句。 惊喜!
引用值( "false"
),引用variables( "$var"
),或者使用test
或[[
而不是[
,不要区别。
我build议:
以下是我build议你检查你的“布尔人”的方法。 他们按预期工作。
bool=true if [ "$bool" = true ]; then if [ "$bool" = "true" ]; then if [[ "$bool" = true ]]; then if [[ "$bool" = "true" ]]; then if [[ "$bool" == true ]]; then if [[ "$bool" == "true" ]]; then if test "$bool" = true; then if test "$bool" = "true"; then
他们都非常相当。 您必须input比其他答案中的方法更多的按键5,但是您的代码将更具防御性。
脚注
- 美库的答案已经被编辑,不再包含(已知)的缺陷。
- 不是一个详尽的清单。
- 在这种情况下有效的命令意味着一个存在的命令。 命令使用是否正确无关紧要。 例如,即使没有这样的手册页,
man woman
仍然被认为是有效的命令。 - 对于无效的(不存在的)命令,Bash只会抱怨命令没有find。
- 如果你在意长度,第一个build议是最短的。
这里似乎有一些关于bash内build的true
误解,更具体地说,bash如何扩展和解释括号内的expression式。
在miku的回答中的代码与true
的命运,也不是true
的命运,也没有任何其他味道完全没有关系。 在这种情况下, true
只不过是一个简单的string,并且既不能通过variables赋值也不能通过对条件expression式的评估来调用true
命令/内build函数。
下面的代码在function上与miku的答案中的代码相同:
the_world_is_flat=yeah if [ "$the_world_is_flat" = yeah ]; then echo 'Be careful not to fall off!' fi
这里唯一的区别是被比较的四个字符是'y','e','a'和'h'而不是't','r','u'和'e'。 而已。 没有任何尝试调用命令或内部命名yeah
,也没有(在miku的例子中)当bash分析令牌为true
时进行的任何特殊处理。 这只是一个string,而完全是任意的。
更新(2/19/2014):在miku回答中的链接之后,现在我看到了一些混淆来自哪里。 Miku的答案使用单个括号,但他链接到的代码片段不使用括号。 只是:
the_world_is_flat=true if $the_world_is_flat; then echo 'Be careful not to fall off!' fi
这两个代码片段的行为方式都是一样的,但括号完全改变了底层的内容。
这里是bash在每种情况下所做的事情:
没有括号:
- 将variables
$the_world_is_flat
展开为string"true"
。 - 尝试将string
"true"
parsing为命令。 - find并运行
true
命令(内置或/bin/true
,根据bash版本)。 - 将
true
命令(始终为0)的退出代码与0进行比较。回想一下,在大多数shell中,退出代码0表示成功,其他任何代码表示失败。 - 由于退出代码是0(成功),执行
if
语句的then
子句
括号:
- 将variables
$the_world_is_flat
展开为string"true"
。 - parsing完全扩展的条件expression式,其forms为
string1 = string2
。=
运算符是bash的string比较运算符 。 所以… - 对
"true"
和"true"
进行string比较。 - 是的,两个string是一样的,所以条件的值是真的。
- 执行
if
语句的then
子句。
无括号的代码工作,因为true
命令返回一个退出码0,这表明成功。 括号内的代码工作,因为$the_world_is_flat
的值与=
右侧的stringtrue
相同。
只是为了将这一点引入家庭,请考虑以下两个代码片段:
此代码(如果以root权限运行)将重新启动您的计算机:
var=reboot if $var; then echo 'Muahahaha! You are going down!' fi
这个代码只是打印“不错的尝试”。 重新启动命令不被调用。
var=reboot if [ $var ]; then echo 'Nice try.' fi
更新(2014年4月14日)要回答评论中关于=
和==
AFAIK之间的区别的问题,没有区别。 ==
运算符是=
的bash特定的同义词,就我所见,它们在所有上下文中都完全相同。 但请注意,我正在专门讨论在[ ]
或[[ ]]
testing中使用的=
和==
string比较运算符。 我并不是build议=
和==
在bash中随处可见 。 例如,你显然不能用==
做variables赋值,比如var=="foo"
(技术上你可以这样做,但是var
的值是"=foo"
,因为bash没有看到==
运算符在这里,它看到一个=
(赋值)运算符,后面是字面值="foo"
,它变成了"=foo"
)。
此外,虽然=
和==
是可以互换的,但是您应该记住,这些testing的工作方式取决于您在[ ]
还是[[ ]]
使用它,也取决于操作数是否被引用。 你可以在这里阅读更多关于它的信息: 7.3其他比较运算符 (向下滚动到=
和==
的讨论)。
使用算术expression式。
#!/bin/bash false=0 true=1 ((false)) && echo false ((true)) && echo true ((!false)) && echo not false ((!true)) && echo not true
输出:
真正
不是假的
很久以前,当我们所有的东西都是sh
,依靠test
程序的约定来处理布尔运算,如果运行没有参数, test
返回一个错误的退出状态。 这使得人们可以将未设置为false的variables和设置为任何值的variables都视为true。 今天,testing内置于bash
,并且通常被称为一个字符别名[
(或者一个可执行文件,用于缺lessshell的可执行文件,如dolmen notes):
FLAG="up or <set>" if [ "$FLAG" ] ; then echo 'Is true' else echo 'Is false' fi # unset FLAG # also works FLAG= if [ "$FLAG" ] ; then echo 'Continues true' else echo 'Turned false' fi
由于引用约定,脚本编写者更喜欢使用复合命令[[
模仿test
但有更好的语法:带空格的variables不需要引用,可以使用&&
和||
作为具有奇怪优先级的逻辑运算符,并且在术语数上没有POSIX限制。
例如,要确定是否设置了FLAG,并且COUNT是大于1的数字:
FLAG="up" COUNT=3 if [[ $FLAG && $COUNT -gt '1' ]] ; then echo 'Flag up, count bigger than 1' else echo 'Nope' fi
当空间,零长度string和空variables都需要时,以及当你的脚本需要使用多个shell时, 这些东西可能会引起混淆 。
如何声明和使用shell脚本中的布尔variables?
与许多其他编程语言不同的是,Bash不会按“types”来分隔variables。 [1]
所以答案很清楚。 在bash中没有boolean variable
。 但是:
使用声明语句,我们可以限制赋值给variables。 [2]
#!/bin/bash declare -ir BOOL=(0 1) #remember BOOL can't be unset till this shell terminate readonly false=${BOOL[0]} readonly true=${BOOL[1]} #same as declare -ir false=0 true=1 ((true)) && echo "True" ((false)) && echo "False" ((!true)) && echo "Not True" ((!false)) && echo "Not false"
declare
和readonly
的r
选项用于明确声明variables是只读的 。 希望目的是明确的。
为什么不使用一个更好的价值而不是真假?
例如:
build_state=success if something-horrible; then build_state=failed fi if [[ "$build_state" == success ]]; then echo go home, you are done else echo your head is on fire, run around in circles fi
比尔·帕克被拒绝了,因为他的定义与正常的代码惯例相反。 通常情况下,true被定义为0,false被定义为非零。 1将会失败,9999和-1也是如此。 与函数返回值相同 – 0表示成功,任何非零表示失败。 对不起,我还没有投票或直接回复他。
Bashbuild议现在使用双括号而不是单个括号,Mike Holt的链接解释了他们如何工作的不同之处。 7.3。 其他比较运算符
有一件事-eq是一个数字运算符,所以有代码
#**** NOTE *** This gives error message ***** The_world_is_flat=0; if [ "${The_world_is_flat}" -eq true ]; then
会发出一个错误语句,期望一个整数expression式。 这适用于任一参数,因为它们都不是整数值。 然而,如果我们把它放在括号内,它不会发出错误声明,但会产生一个错误的值(在50%的可能排列中)。 它会评估为[[0 -eq true]] =成功,而且[[0 -eq false]] =成功,这是错误的(嗯……那个内build值是一个数值?)。
#**** NOTE *** This gives wrong output ***** The_world_is_flat=true; if [[ "${The_world_is_flat}" -eq true ]]; then
有条件的其他排列也会给出错误的输出。 基本上,任何(除了上面列出的错误条件)将variables设置为数值并将其与真/假内build比较,或将variables设置为真/假内build并将其与数值进行比较。 另外,任何将variables设置为true / false的内build,并使用-eq进行比较。 因此,避免使用-eq进行布尔比较,避免使用数值进行布尔比较。 以下是将导致无效结果的排列的摘要:
#With variable set as an integer and evaluating to true/false #*** This will issue error warning and not run: ***** The_world_is_flat=0; if [ "${The_world_is_flat}" -eq true ]; then #With variable set as an integer and evaluating to true/false #*** These statements will not evaluate properly: ***** The_world_is_flat=0; if [ "${The_world_is_flat}" -eq true ]; then # if [[ "${The_world_is_flat}" -eq true ]]; then # if [ "${The_world_is_flat}" = true ]; then # if [[ "${The_world_is_flat}" = true ]]; then # if [ "${The_world_is_flat}" == true ]; then # if [[ "${The_world_is_flat}" == true ]]; then #With variable set as an true/false builtin and evaluating to true/false #*** These statements will not evaluate properly: ***** The_world_is_flat=true; if [[ "${The_world_is_flat}" -eq true ]]; then # if [ "${The_world_is_flat}" = 0 ]; then # if [[ "${The_world_is_flat}" = 0 ]]; then # if [ "${The_world_is_flat}" == 0 ]; then # if [[ "${The_world_is_flat}" == 0 ]]; then
所以,现在到什么工作。 对于比较和评估,使用true / false内build函数(正如Mike Hunt所指出的,不要将它们用引号括起来)。 然后使用或者单或双等号(=或==)和单或双括号([]或[[]])。 就我个人而言,我喜欢双等号,因为它让我想起了其他编程语言中的逻辑比较,而双引号就是因为我喜欢打字。 所以这些工作:
#With variable set as an integer and evaluating to true/false #*** These statements will work properly: ***** # The_world_is_flat=true/false; if [ "${The_world_is_flat}" = true ]; then # if [[ "${The_world_is_flat}" = true ]]; then # if [ "${The_world_is_flat}" = true ]; then # if [[ "${The_world_is_flat}" == true ]]; then
你有它。
早上温柔的民间。 我发现现有的select混淆。 就我个人而言,我只想拥有像C一样的东西。这对我来说是有效的。
# snapshotEvents=true : if ($snapshotEvents); then # do stuff fi
并保持每个人的快乐,我testing:
# snapshotEvents=false : if !($snapshotEvents); then # do else stuff fi
这也工作得很好。
$snapshotEvents
评估variables的值(内容)。 你不需要括号,我只是觉得他们很有帮助。
- testing:GNU Bash,版本4.1.11(2) – 发布
- Bash入门指南 ,Machtelt Garrels,v1.11,2008
长话短说:
你没有bash中的布尔值
bash所具有的是比较和条件下的布尔expression式。 也就是说,你可以在bash中声明和比较的是string和数字。 而已。
无论你在bash中看到的是true
还是false
,它都是一个string或者是一个只用于退出代码的命令/内build函数。
这个语法…
if true; then ...
基本上…
if COMMAND; then ...
只要命令返回退出代码0,条件为真。
你也可以这样做:
if which foo; then echo "program foo found"; fi
使用方括号或test
命令时,您依赖于该构造的退出代码。 请记住, [ ]
和[[ ]]
也只是命令/内置类似的任何其他。 所以…
if [[ 1 == 1 ]]; then echo yes; fi
只是语法糖…
[[ 1 == 1 ]] && echo yes
所以当在上述任何一个结构中使用true
和false
,实际上只是将string"true"
或"false"
传递给testing命令。 这里是一个例子:
信不信由你,这些条件都产生了同样的结果 :
if [[ false ]]; then ... if [[ "false" ]]; then ... if [[ true ]]; then ... if [[ "true" ]]; then ...
TL; DR; 总是比较string或数字
为了使未来的读者清楚这一点,我会build议总是用true
和false
引号:
做
if [[ "${var}" == "true" ]]; then ... if [[ "${var}" == "false" ]]; then ... if [[ -n "${var:-}" ]]; then echo "var is not empty" ...
别
if [ ... ]; then ... # always use double square brackets in bash! if [[ "${var}" ]]; then ... # this is not as clear or searchable as -n if [[ "${var}" != true ]]; then ... # creates impression of booleans if [[ "${var}" != "true" ]]; then ... # creates impression of booleans. Better compare against "false" here if [[ "${var}" -eq "true" ]]; then ... # `-eq` is for numbers and doesn't read as easy as `==`
这是对@ miku的原始答案的一个改进,它解决了@dennis关于未设置variables的情况:
the_world_is_flat=true # ...do something interesting... if ${the_world_is_flat:-false} ; then echo 'Be careful not to fall off!' fi
要testing该variables是否为false:
if ! ${the_world_is_flat:-false} ; then echo 'Be careful not to fall off!' fi
关于其他情况(variables中令人讨厌的内容),这是任何外部input提供给程序的问题。 任何外部input必须在信任之前进行validation。 但是当收到这个input的时候,validation只需要做一次 。 它不必像@dennis所build议的那样,在每次使用variables时都要影响程序的性能。
Bash真的把这个问题弄糊涂了,[,[(,((,)$(等等)都在践踏着对方的代码空间,我想这大部分是历史的,bash偶尔会假装是sh。
大多数时候,我可以select一种方法并坚持下去。 在这种情况下,我倾向于声明(最好在一个公共库文件中,我可以包含在我的实际脚本中)
TRUE = 1; FALSE = 0
然后,我可以使用((算术))比较运算符来testing… …
的testvar = $ FALSE if [[-d $ {does_directory_exist}]]; 那么testvar = $ TRUE; 科幻 if((testvar == TRUE)); 然后 #do stuff'cos目录确实存在 。 。 。 科幻
- 您必须遵守纪律,您的testvar必须始终设置为$ TRUE或$ FALSE
- 在(())比较器中,不需要前面的$,这使得它更具可读性
- 我可以使用(()),因为$ TRUE = 1,$ FALSE = 0,即数字
- 缺点是不得不偶尔使用$
的testvar = $ TRUE
...which is not so pretty.
这不是一个完美的解决scheme,但它涵盖了每一个我需要这样一个testing的情况…所以我对它感到满意。