如何声明和使用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,但是您的代码将更具防御性。


脚注

  1. 美库的答案已经被编辑,不再包含(已知)的缺陷。
  2. 不是一个详尽的清单。
  3. 在这种情况下有效的命令意味着一个存在的命令。 命令使用是否正确无关紧要。 例如,即使没有这样的手册页, man woman仍然被认为是有效的命令。
  4. 对于无效的(不存在的)命令,Bash只会抱怨命令没有find。
  5. 如果你在意长度,第一个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在每种情况下所做的事情:

没有括号:

  1. 将variables$the_world_is_flat展开为string"true"
  2. 尝试将string"true"parsing为命令。
  3. find并运行true命令(内置或/bin/true ,根据bash版本)。
  4. true命令(始终为0)的退出代码与0进行比较。回想一下,在大多数shell中,退出代码0表示成功,其他任何代码表示失败。
  5. 由于退出代码是0(成功),执行if语句的then子句

括号:

  1. 将variables$the_world_is_flat展开为string"true"
  2. parsing完全扩展的条件expression式,其forms为string1 = string2=运算符是bash的string比较运算符 。 所以…
  3. "true""true"进行string比较。
  4. 是的,两个string是一样的,所以条件的值是真的。
  5. 执行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" 

declarereadonlyr选项用于明确声明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 

所以当在上述任何一个结构中使用truefalse ,实际上只是将string"true""false"传递给testing命令。 这里是一个例子:

信不信由你,这些条件都产生了同样的结果

 if [[ false ]]; then ... if [[ "false" ]]; then ... if [[ true ]]; then ... if [[ "true" ]]; then ... 

TL; DR; 总是比较string或数字

为了使未来的读者清楚这一点,我会build议总是用truefalse引号:

 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目录确实存在
 。
 。
 。
科幻 
  1. 您必须遵守纪律,您的testvar必须始终设置为$ TRUE或$ FALSE
  2. 在(())比较器中,不需要前面的$,这使得它更具可读性
  3. 我可以使用(()),因为$ TRUE = 1,$ FALSE = 0,即数字
  4. 缺点是不得不偶尔使用$
 的testvar = $ TRUE 
 ...which is not so pretty. 

这不是一个完美的解决scheme,但它涵盖了每一个我需要这样一个testing的情况…所以我对它感到满意。