Bash运算符[[vs [vs(vs((??


243

对于在bash中使用这些运算符(括号,双括号,括号和双括号)有何不同之处,我有些困惑。

[[ , [ , ( , ((

我见过人们在if语句上使用它们,例如:

if [[condition]]

if [condition]

if ((condition))

if (condition)



3
@cuonglm讽刺,因为该链接收益率这个问题,作为第一个结果。悖论!
疯狂的

5
我想阅读文档不是一种选择吗?
Lightness Races in Orbit

34
括号和方括号在文档中不太容易搜索,如果您不知道这些功能的名称,那么便拥有这些。
ilkkachu

Answers:


255

一条if语句通常看起来像

if commands1
then
   commands2
else
   commands3
fi

then如果退出码commands1为零,则执行该子句。如果退出代码为非零,则else执行该子句。 commands1可以是简单的也可以是复杂的。它可以,例如,是由操作者的一个分隔的一个或多个管道,一个序列;&&&,或||。下面if显示的条件仅是以下情况的特例commands1

  1. if [ condition ]

    这是传统的shell test命令。它在所有POSIX外壳上均可用。测试命令设置退出代码,该if语句相应地起作用。典型的测试是文件是否存在或一个数字等于另一个。

  2. if [[ condition ]]

    这是一个新的升级变化testKSH抨击zsh中也支持。该test命令还设置了退出代码,该if语句相应地起作用。在其扩展功能中,它可以测试字符串是否与正则表达式匹配。

  3. if ((condition))

    bashzsh也支持的另一个ksh扩展。这执行算术运算。算术的结果是,设置了退出代码,该语句相应地起作用。如果算术计算的结果为非零,则返回退出代码零(true)。与一样,此表单不是POSIX,因此不可移植。if[[...]]

  4. if (command)

    这在子shell中运行命令。命令完成后,它将设置退出代码,并且该if语句将相应地执行操作。

    使用这样的子shell的一个典型原因是限制副作用(command如果command需要的话)或对shell环境进行其他更改。子外壳程序完成后,这些更改不会保留。

  5. if command

    命令执行后,该if语句根据其退出代码执行操作。


24
感谢您加入第5个选项。这是理解其实际工作方式的关键,而且利用不足。
小鸡

4
请注意,[它实际上是二进制文件,而不是内部命令或符号。一般居住在/bin
朱利安

8
@JulienR。实际上[是内置的test。由于兼容性原因,有二进制版本可用。签出help [help test
OldTimer

4
值得一提的是,虽然(((不是POSIX,$((即算术扩展,而且很容易混淆它们。通常,一种解决方法是在类似条件的[ $((2+2)) -eq 4 ]语句中使用算术
Sergiy Kolodyazhnyy

如何获得乐趣打印新行:if echo; then echo; fi
AnthonyD973 '18

77
  • (…)括号表示子壳。它们里面的东西并不像许多其他语言那样表达。这是命令列表(就像外括号一样)。这些命令在单独的子过程中执行,因此在括号内执行的任何重定向,赋值等在括号外均无效。
    • 带有前导美元符号的$(…)命令的替换:括号内有一个命令,并且命令的输出用作命令行的一部分(经过额外的扩展,除非替换在双引号之间,但这是另一个故事) 。
  • { … }大括号就像括号一样,因为它们将命令分组,但是它们仅影响语法分析,而不影响分组。该程序x=2; { x=4; }; echo $x打印4,而x=2; (x=4); echo $x打印2。(还必须在命令位置将括号括起来作为关键字,并在命令位置找到(因此,after {;before之前的空格}),而括号则没有。这只是语法上的怪异。)
    • 带有前导美元符号的${VAR}参数扩展,扩展为变量的值,并可能进行额外的转换。该ksh93外壳程序还支持${ cmd;}不产生子外壳程序的命令替换形式。
  • ((…))双括号包围着算术指令,即对整数的计算,其语法类似于其他编程语言。此语法主要用于赋值和条件句中。它仅存在于ksh / bash / zsh中,不存在于纯sh中。
    • 算术表达式使用相同的语法$((…)),该语法扩展为表达式的整数值。
  • [ … ]单括号包围条件表达式。条件表达式主要建立在运算符上,例如-n "$variable"测试变量是否为空以及-e "$file"测试文件是否存在。请注意,您需要在每个运算符周围加一个空格(例如[ "$x" = "$y" ],not [ "$x"="$y" ]),并;在方括号内和外都需要一个空格或字符(例如[ -n "$foo" ],not [-n "$foo"])。
  • [[ … ]]双括号是ksh / bash / zsh中条件表达式的另一种形式,具有一些附加功能,例如,您可以编写[[ -L $file && -f $file ]]测试文件是否是指向常规文件的符号链接,而单括号需要[ -L "$file" ] && [ -f "$file" ]。请参见为什么用带引号的空格进行参数扩展在双括号[[而不是单括号[?有关此主题的更多信息。

在shell中,每个命令都是条件命令:每个命令的返回状态为0表示成功,或者为1到255之间的整数(在某些shell中可能更大)表示失败。的[ … ]命令(或[[ … ]]语法形式)是一个特定的命令,该命令也可以被拼写test …,并且当一个文件存在成功,或当一个字符串非空时,或者当数比另一个较小的,等等。((…))语法形式成功时的数不为零。以下是一些Shell脚本中的条件语句示例:

  • 测试是否myfile包含字符串hello

    if grep -q hello myfile; then 
  • 如果mydir是目录,请切换到该目录并执行以下操作:

    if cd mydir; then
      echo "Creating mydir/myfile"
      echo 'some content' >myfile
    else
      echo >&2 "Fatal error. This script requires mydir to exist."
    fi
  • 测试myfile当前目录中是否存在一个名为的文件:

    if [ -e myfile ]; then 
  • 相同,但还包括悬挂的符号链接:

    if [ -e myfile ] || [ -L myfile ]; then 
  • 测试x(假定为数字)的值是否至少为2,可移植:

    if [ "$x" -ge 2 ]; then 
  • 测试xbash / ksh / zsh 中(假定为数字)的值是否至少为2:

    if ((x >= 2)); then 

请注意,单个括号支持-a而不是&&,因此可以这样写:[ -L $file -a -f $file ],即括号内的字符数相同,没有多余的[]...
Alexis Wilke

6
@AlexisWilke运算符-a-o是有问题的,因为如果涉及的某些操作数看起来像运算符,则它们可能导致错误的解析。这就是为什么我没有提到它们:它们的优势为零,而且并不总是有效。而且也不要在没有充分理由的情况下写未加引号的变量扩展名:[[ -L $file -a -f $file ]]很好,但是需要使用单括号[ -L "$file" -a -f "$file" ](可以,例如,$file始终以/或开头./)。
吉尔斯

请注意,它[[ -L $file && -f $file ]](不-a包含[[...]]变体)。
斯特凡Chazelas

18

bash文档中

(list)该列表在子外壳程序环境中执行(请参见下面的“命令执行环境”)。在命令完成后,影响外壳环境的变量分配和内置命令将保持无效。返回状态是列表的退出状态。

换句话说,您要确保'list'中发生的任何事情(如a cd)在(and 之外均无效)。将泄漏的唯一事情是最后的命令或与退出代码set -e产生一个错误(大于几如其它的第一个命令ifwhile等等)

((expression))根据以下算术评估中描述的规则评估表达。如果表达式的值不为零,则返回状态为0;否则,返回值为0。否则返回状态为1。这完全等同于let“表达式”。

这是一个bash扩展,允许您进行数学运算。这有点类似于expr没有所有限制的使用expr(例如到处都有空格,转义*等)

[[ expression ]]根据条件表达式的计算结果,返回状态0或1。表达式由以下条件表达式中描述的基元组成。不对[[和]]之间的单词进行单词拆分和路径名扩展;执行波浪号扩展,参数和变量扩展,算术扩展,命令替换,进程替换和引用删除。必须取消引用-f之类的条件运算符,才能将其识别为主键。

与[[一起使用时,<和>运算符使用当前语言环境按字典顺序排序。

这提供了一个高级测试来比较字符串,数字和文件,有点像商品test,但功能更强大。

[ expr ]根据条件表达式expr的计算结果,返回状态0(真)或1(假)。每个运算符和oper和必须是一个单独的参数。表达式由上面条件表达式中描述的基元组成。测试不接受任何选项,也不接受和忽略-的参数,表示选项结束。

[...]

这个叫test。实际上,在过去,这[是的符号链接test。它的工作方式相同,您也有相同的限制。由于二进制文件知道启动它的名称,因此测试程序可以解析参数,直到找到一个parameter为止]。有趣的Unix技巧。

请注意,在和中bash,它们是内置函数(如注释中所述),但几乎有相同的限制。[test


1
虽然test并且[当然是Bash中的内置命令,但是很可能也存在一个外部二进制文件。
ilkkachu

1
外部二进制文件[不是test大多数现代系统上的符号链接。
Random832 '16

1
不知何故,我发现它们烦恼地创建两个单独的二进制文件,这两个二进制文件恰好具有它们所需要的,而不是仅仅组合它们并添加几个条件。尽管实际上也strings /usr/bin/test显示了帮助文本,所以我不知道该说些什么。
ilkkachu 2016年

2
@ Random832我对GNU的基本原理可以避免出现意外的arg0行为,但对于POSIX要求,我不是很肯定。虽然标准test显然要求该命令以基于独立文件的命令的形式存在,但其中没有任何内容指出该[变量也需要以这种方式实现。例如,Solaris 11不提供任何[可执行文件,但仍完全符合POSIX标准
jlliagre

2
(出口1)在括号之外具有作用。
亚历山大

14

[[[

该答案将涵盖问题的[vs [[子集。

Bash 4.3.11的一些区别:

  • POSIX vs Bash扩展:

  • 常规命令与魔术

    • [ 只是一个带有奇怪名称的常规命令。

      ]只是其中的一个论点而[无法使用进一步的论点。

      Ubuntu 16.04实际上/usr/bin/[由coreutils提供了一个可执行文件,但是bash内置版本优先。

      Bash解析命令的方式没有任何改变。

      特别<是重定向,&&||连接多个命令,( )除非通过进行转义\,否则将生成子外壳,并且单词扩展将照常进行。

    • [[ X ]]是一个X可以神奇地解析的单一构造。<&&||()经特殊处理,以及分词规则是不同的。

      还有进一步的差异,例如==~

      在Bashese中:[是一个内置命令,并且[[是一个关键字:https : //askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword

  • <

  • &&||

    • [[ a = a && b = b ]]:真实,逻辑
    • [ a = a && b = b ]:语法错误,&&解析为AND命令分隔符cmd1 && cmd2
    • [ a = a -a b = b ]:等效,但已被POSIX³弃用
    • [ a = a ] && [ b = b ]:POSIX和可靠的等效产品
  • (

    • [[ (a = a || a = b) && a = b ]]:错误
    • [ ( a = a ) ]:语法错误,()被解释为子shell
    • [ \( a = a -o a = b \) -a a = b ]:等效,但()已被POSIX弃用
    • { [ a = a ] || [ a = b ]; } && [ a = b ]POSIX等效5
  • 扩展时分词和生成文件名(split + glob)

    • x='a b'; [[ $x = 'a b' ]]:正确,不需要引号
    • x='a b'; [ $x = 'a b' ]:语法错误,扩展为 [ a b = 'a b' ]
    • x='*'; [ $x = 'a b' ]:如果当前目录中有多个文件,则语法错误。
    • x='a b'; [ "$x" = 'a b' ]:POSIX等效
  • =

    • [[ ab = a? ]]:是的,因为它确实进行模式匹配* ? [很神奇)。不会将glob扩展到当前目录中的文件。
    • [ ab = a? ]a?glob扩展。因此,根据当前目录中的文件,可能是true还是false。
    • [ ab = a\? ]:错误,不是全局扩展
    • ===在双方的同[[[,不过==是一个bash扩展。
    • case ab in (a?) echo match; esac:POSIX等效
    • [[ ab =~ 'ab?' ]]:错误4,失去魔法''
    • [[ ab? =~ 'ab?' ]]:真
  • =~

    • [[ ab =~ ab? ]]:true,POSIX 扩展正则表达式匹配,?不全局扩展
    • [ a =~ a ]:语法错误。没有bash等效项。
    • printf 'ab\n' | grep -Eq 'ab?':等效于POSIX(仅单行数据)
    • awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?':等同于POSIX。

建议:经常使用[]

[[ ]]我见过的每个构造都有POSIX等效项。

如果您使用[[ ]]

  • 失去便携性
  • 迫使读者学习另一个bash扩展的复杂性。[只是一个带有奇怪名称的常规命令,不涉及任何特殊的语义。

¹源自[[...]]Korn外壳中的等效构造

²,但对于aor的某些值b(例如+index)失败,并且如果ab看起来像十进制整数,则进行数字比较。expr "x$a" '<' "x$b"都可以解决。

³也失败了的一些值ab喜欢!(

在bash 3.2及更高版本中为4,并且未提供与bash 3.1的兼容性(与一样BASH_COMPAT=3.1

5,尽管分组(此处使用{...;}命令组而不是(...)运行不必要的子shell)不是必需的,因为||and &&壳运算符(与||and && [[...]]运算符或-o/ -a [运算符相对)具有相同的优先级。因此[ a = a ] || [ a = b ] && [ a = b ]将是等效的。


@StéphaneChazelas感谢您的信息!我已经添加expr了答案。术语“ Bash扩展”并不意味着Bash是第一个添加一些语法的Shell,学习POSIX sh vs Bash已经足以使我发疯。
Ciro Santilli新疆改造中心法轮功六四事件

看看man test您是否尝试过man [并且迷路了。这将解释POSIX变体。
Jonathan Komar

13

一些例子:

传统测试:

foo="some thing"
# check if value of foo is not empty
if [ -n "$foo" ] ; then... 
if test -n "$foo" ; then... 

test并且[是与其他命令一样的命令,因此,除非将其括在引号中,否则变量会被拆分为多个单词。

新型测试

[[ ... ]] 是一个(较新的)特殊的shell构造,其工作方式略有不同,最明显的是它没有单词分割的变量:

if [[ -n $foo ]] ; then... 

此处的一些文档[[[

算术测试:

foo=12 bar=3
if (( $foo + $bar == 15 )) ; then ...  

“普通”命令:

上面的所有操作都像普通命令一样,并且if可以采用任何命令:

# grep returns true if it finds something
if grep pattern file ; then ...

多个命令:

或者我们可以使用多个命令。包装一组命令( ... )将它们运行在子shell中,从而创建shell状态(工作目录,变量)的临时副本。如果我们需要在另一个目录中临时运行某些程序:

# this will move to $somedir only for the duration of the subshell 
if ( cd $somedir ; some_test ) ; then ...

# while here, the rest of the script will see the new working
# directory, even after the test
if cd $somedir ; some_test ; then ...

1

分组命令

Bash提供了两种方法来对要作为一个单元执行的命令列表进行分组。

( list )将命令列表放在括号之间会导致创建一个子外壳环境,并且列表中的每个命令都将在该子外壳中执行。由于列表是在子Shell中执行的,因此在子Shell完成后变量分配不会保持有效。

$ a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a"
inside: a=2
outside: a=1

{ list; }在大括号之间放置命令列表会导致该列表在当前Shell上下文中执行。没有创建子外壳。以下列表是分号(或换行符)。资源

${} Parameter expansion Ex:  ANIMAL=duck; echo One $ANIMAL, two ${ANIMAL}s
$() Command substitution Ex: result=$(COMMAND) 
$(()) Arithmetic expansion Ex: var=$(( 20 + 5 )) 

条件构造

单括号,[]
用于比较==, !=, <,>应该用于数值比较eq, ne,ltgt应该用于。

增强型支架,[[]]

在以上所有示例中,我们仅使用单括号将条件表达式括起来,但是bash允许使用双括号,这是单括号语法的增强版本。

为了进行比较==, !=, <,>可以原样使用。

  • [是测试命令的同义词。即使将其内置到外壳中,它也会创建一个新进程。
  • [[ 是它的新改进版本,它是关键字,而不是程序。
  • [[Korn和理解Bash

资源

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.