Bash中的简单逻辑运算符


255

我有几个变量,我想检查以下情况(用文字写出来,然后尝试bash脚本失败):

if varA EQUALS 1 AND ( varB EQUALS "t1" OR varB EQUALS "t2" ) then 

do something

done.

在失败的尝试中,我想到了:

if (($varA == 1)) && ( (($varB == "t1")) || (($varC == "t2")) ); 
  then
    scale=0.05
  fi

Answers:


681

您编写的内容实际上几乎可以运行(如果所有变量均为数字,则可以运行),但这根本不是惯用的方式。

  • (…)括号表示子壳。它们里面的东西并不像许多其他语言那样表达。这是命令列表(就像外括号一样)。这些命令在单独的子过程中执行,因此在括号内执行的任何重定向,赋值等在括号外均无效。
    • 带有前导美元符号的$(…)命令的替换:括号内有一个命令,并且命令的输出用作命令行的一部分(经过额外的扩展,除非替换在双引号之间,但这是另一个故事) 。
  • { … }花括号类似于括号,因为它们将命令分组,但是它们仅影响语法分析,而不影响分组。该程序将x=2; { x=4; }; echo $x打印4,而将x=2; (x=4); echo $x打印2。(大括号在闭合之前也需要在其周围留一个空格和一个分号,而括号则不需要。这只是一个语法怪异。)
    • 带有前导美元符号的${VAR}参数扩展,扩展为变量的值,并可能进行额外的转换。
  • ((…))双括号包围着算术指令,即对整数的计算,其语法类似于其他编程语言。此语法主要用于赋值和条件句中。
    • 算术表达式使用相同的语法$((…)),该语法扩展为表达式的整数值。
  • [[ … ]]双括号包围条件表达式。条件表达式主要建立在运算符上,例如-n $variable测试变量是否为空以及-e $file测试文件是否存在。还有字符串相等运算符:"$string1" == "$string2"(提防右手边是一个模式,如[[ $foo == a* ]]检测是否$foo开始与a同时[[ $foo == "a*" ]]测试,如果$foo是完全a*),和熟悉!&&以及||对否定,合和脱节,以及对分组圆括号运营商。请注意,您需要在每个运算符周围加一个空格(例如[[ "$x" == "$y" ]],not [[ "$x"=="$y" ]]),并;在方括号内和外都需要一个空格或字符(例如[[ -n $foo ]][[-n $foo]])。
  • [ … ]单括号是条件表达式的另一种形式,具有更多怪癖(但较旧且更易于移植)。现在不要写任何东西;当您发现包含它们的脚本时,就开始担心它们。

这是在bash中编写测试的惯用方式:

if [[ $varA == 1 && ($varB == "t1" || $varC == "t2") ]]; then

如果您需要移植到其他外壳,可以采用这种方式(请注意每个单独测试的附加引号和单独的括号,并使用传统的=运算符,而不是ksh / bash / zsh ==变体):

if [ "$varA" = 1 ] && { [ "$varB" = "t1" ] || [ "$varC" = "t2" ]; }; then

31
很棒的帖子,括号总结很理想。
KomodoDave

10
最好使用==区分变量和比较的方法(也是=
Will Sheppard 2014年

1
哦,我的意思是单(圆)括号,对于造成的混乱,我们深表歉意。[[ $varA = 1 && ($varB = "t1" || $varC = "t2") ]]尽管第一个要点明确指出:“ [括号]内的内容不像许多其他语言一样,但表达式 ”中的那些并没有启动子过程-但肯定在这里!对于经验丰富的bash向导来说,这可能是显而易见的,但对我而言,甚至不是立即。之所以会引起混淆,是因为在语句中可以使用单括号if,而不能在双括号内的表达式中使用。
彼得-恢复莫妮卡

2
@protagonist ==并不是真的比“更惯用” =。它们具有相同的含义,但是==是ksh的变体,也可以在bash和zsh中使用,而它们=是可移植的。使用确实没有任何优势==,并且在普通模式下也不起作用。
吉尔(Gilles)'所以

2
@WillSheppard比较和赋值之间已经有许多其他区别:比较放在方括号内,赋值不在;比较运算符周围有空格,赋值没有;赋值在左边有一个变量名,比较很少会在左边有一个看起来像变量名的东西,无论如何,您都可以并且应该加上引号。如果愿意,您可以==在内部编写文字[[ … ]],但=同时也可以使用[ … ],因此,我建议完全不要养成使用的习惯==
吉尔斯(Gillles)“所以-别再邪恶了”

34

很接近

if [[ $varA -eq 1 ]] && [[ $varB == 't1' || $varC == 't2' ]]; 
  then 
    scale=0.05
  fi

应该管用。

分解

[[ $varA -eq 1 ]] 

是整数比较,其中as

$varB == 't1'

是字符串比较。否则,我只是将比较正确地分组。

双方括号定界了条件表达式。而且,我发现以下内容是该主题的不错的读物:“(IBM)Demystify test,[,[[,((,and if-then-else“


只是要确保:引用't1'是不必要的,对吧?因为相对于在双括号算术指令,其中t1将是可变的,t1在双条件表达式括号仅仅是一个文字串。即[[ $varB == 't1' ]]与完全相同[[ $varB == t1 ]],对吧?
彼得-恢复莫妮卡

6

一个非常可移植的版本(甚至可以用于旧版的bourne shell):

if [ "$varA" = 1 -a \( "$varB" = "t1" -o "$varB" = "t2" \) ]
then    do-something
fi

这具有额外的质量[,无论外壳的口味如何,最多只能运行一个子过程(即)。

如果变量包含数值=-eq则替换为,例如

  • 3 -eq 03 是真的,但是
  • 3 = 03是假的。(字符串比较)

3

以下是if-then-else语句的简短版本的代码:

( [ $a -eq 1 ] || [ $b -eq 2 ] ) && echo "ok" || echo "nok"

请注意以下几点:

  1. ||&&条件内部的操作数(即圆括号之间)是逻辑操作数(或/和)

  2. ||&&操作数在外部,如果条件意味着则/否则

实际上,该声明说:

如果(a = 1或b = 2),则“确定”,否则“否”


括号( ... )创建一个子外壳。可能想用大括号{ ... }代替。在子shell中创建的任何状态在调用者中都不可见。
克林特·帕奇

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.