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

[[ , [ , ( , ((


我见过人们在这样的if语句上使用它们:

if [[condition]]

if [condition]

if ((condition))

if (condition)


评论

相关:使用单括号或双括号– bash

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

#1 楼

在类似Bourne的shell中,如果if命令列表的退出代码为零,则then语句通常看起来像
if
   command-list1
then
   command-list2
else
   command-list3
fi

执行command-list1子句。如果退出代码为非零,则执行else子句。 command-list1可以是简单的也可以是复杂的。例如,它可以是由一个运算符;&&&||或换行符之一分隔的一个或多个管道的序列。下面显示的if条件只是command-list1的特例:


if [ condition ]
[是传统test命令的另一个名称。 [ / test是标准的POSIX实用程序。所有POSIX外壳程序都内置了它(尽管POSIX²不需要)。 test命令设置退出代码,并且if语句相应地起作用。典型的测试是文件是否存在或一个数字是否等于另一个数字。


if [[ condition ]]
这是ksh对test¹的新升级版本,其中bash,zsh,yash, busybox sh也支持。此[[ ... ]]构造还设置了退出代码,并且if语句相应地起作用。在其扩展功能中,它可以测试字符串是否匹配通配符模式(不在busybox sh中)。

if ((condition))
bash和zsh也支持的另一个ksh扩展。这执行算术运算。算术的结果是,设置了退出代码,并且if语句相应地起作用。如果算术计算的结果为非零,则返回零(真)的退出码。像[[...]]一样,此格式也不是POSIX,因此也不是可移植的。


if (command)
这在子shell中运行命令。命令完成后,它将设置退出代码,并且if语句将相应地执行操作。
使用此类子外壳的典型原因是,如果command需要对外壳环境进行变量分配或其他更改,则可以限制command的副作用。子外壳程序完成后,此类更改不会保留。


if command
命令已执行,并且if语句根据其退出代码执行操作。



¹虽然不是真正的命令,但是是一种特殊的shell构造,其语法与普通命令的语法不同,并且在shell实现之间存在很大差异。
POSIX确实需要有一个独立的test和但是,系统上的[实用程序,尽管对于[来说,已知有多个Linux发行版缺少它。

评论


感谢您加入第5个选项。这是了解其实际工作方式以及未得到充分利用的关键。

–小鸡
16年8月30日在19:32

请注意[实际上是二进制文件,而不是内部命令或符号。通常居住在/ bin中。

–朱利安·R。
16-09-3在12:17



@JulienR。实际上[是内置的,测试也是。由于兼容性原因,有二进制版本可用。查看帮助[并进行测试。

– OldTimer
16-09-20在4:23

值得注意的是,虽然(((is POSIX,$((即算术扩展是,很容易混淆它们。通常,一种解决方法是使用[$((2 + 2))-eq 4]之类的东西)情节陈述

– Sergiy Kolodyazhnyy
17年11月30日在18:10

我希望我可以多次投票赞成这个答案。完美的解释。

–安东尼·加特林
19-10-9在11:33

#2 楼



(…)括号表示一个子外壳。它们里面的东西并不像许多其他语言那样表达。这是命令列表(就像外括号一样)。这些命令在单独的子过程中执行,因此括号内执行的任何重定向,赋值等在括号外均无效。


带有前导美元符号的$(…)是命令替换:括号内有一个命令,命令的输出用作命令行的一部分(额外扩展后,除非替换在双引号之间,但这是另一回事)。



{ … }大括号就像括号一样,因为它们将命令分组,但是它们仅影响语法分析,而不影响分组。程序x=2; { x=4; }; echo $x打印4,而x=2; (x=4); echo $x打印2。(另外,括号也需要定界,并在命令位置找到(因此,在{之后的空间和;之前的}的空间),而括号没有。这只是一个语法怪异。)


${VAR}具有前导美元符号,是一种参数扩展,可以扩展为变量的值,并可能进行其他转换。 ksh93外壳程序还支持${ cmd;}作为命令替换的形式,该命令替换形式不会生成子shell。



((…))双括号括在算术指令周围,即对整数进行计算,其语法类似于其他编程语言。此语法主要用于赋值和条件句中。仅在ksh / bash / zsh中存在,而在普通sh中不存在。


算术表达式$((…))使用相同的语法,该语法扩展为表达式的整数值。 >


[ … ]单括号括起来的条件表达式。条件表达式主要建立在诸如-n "$variable"之类的运算符上,以测试变量是否为空,而-e "$file"则用于测试文件是否存在。请注意,您需要在每个运算符周围留一个空格(例如[ "$x" = "$y" ]而不是[ "$x"="$y" ]),并且在括号内外都需要一个空格或类似;的字符(例如[ -n "$foo" ]而不是[-n "$foo"])。

[[ … ]]双括号是ksh / bash / zsh中条件表达式的替代形式,具有一些附加功能,例如,您可以编写[[ -L $file && -f $file ]]来测试文件是否是指向常规文件的符号链接,而单括号要求[ -L "$file" ] && [ -f "$file" ]。请参见为什么用带引号的空格进行参数扩展在双括号[[而不是单括号[?有关此主题的更多信息。

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



测试((…))是否包含字符串myfile

if grep -q hello myfile; then …



如果hello是目录,请切换到该目录并执行以下操作:

if cd mydir; then
  echo "Creating mydir/myfile"
  echo 'some content' >myfile
else
  echo >&2 "Fatal error. This script requires mydir to exist."
fi



测试是否存在一个名为mydir的文件当前目录:

if [ -e myfile ]; then …



相同,但还包括悬挂的符号链接:

if [ -e myfile ] || [ -L myfile ]; then …



测试myfile(假定为数字)的值是否至少为2,可移植:

if [ "$x" -ge 2 ]; then …



在bash / ksh / zsh中测试x(假定为数字)的值是否至少为2:

if ((x >= 2)); then …




评论


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

– Alexis Wilke
16年8月27日在23:34

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

–吉尔斯'所以-不再是邪恶的'
16-8-27在23:38



请注意,它是[[-L $ file && -f $ file]](不带[[...]]变体的-a)。

–StéphaneChazelas
17年11月30日在15:25

#3 楼

[ vs [[

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

Bash 4.3.11上的某些区别:



POSIX vs Bash扩展:



[是POSIX


[[是Bash扩展¹,记录在: https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructs




常规命令vs魔术



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

]只是[的自变量,可以防止使用其他自变量。 >
Ubuntu 16.04实际上在coreutils提供的/usr/bin/[处有一个可执行文件,但是bash内置版本优先。

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

特别地,<是重定向的,&&||连接多个命令,( )生成子shell除非es以\为上限,并且单词扩展照常进行。


[[ X ]]是使X能够被神奇地解析的单个结构。 <&&||()经过特殊处理,并且分词规则不同。

还有进一步的区别,例如==~。内置命令,并且[是关键字:https://askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword




[[



<:字典比较

[[ a < b ]]:与上述相同。 [ a \< b ]是必需的,否则像其他任何命令一样进行重定向。 Bash扩展。

\:等效POSIX²,请参见:https://stackoverflow.com/questions/21294867/how-to-test-strings-for-lexicographic-less-than-or-equal- in-bash / 52707989#52707989




expr a \< b > /dev/null&&



||:真,逻辑和


[[ a = a && b = b ]]:语法错误,[ a = a && b = b ]被解析为AND命令分隔符&&

cmd1 && cmd2:等效,但已被POSIX³弃用。

[ a = a -a b = b ]:POSIX和可靠的等效物



>


[ a = a ] && [ b = b ]:假

(:语法错误,[[ (a = a || a = b) && a = b ]]被解释为子外壳POSIX不推荐使用

[ ( a = a ) ] POSIX等效5





扩展时拆分单词和生成文件名(split + glob)



():true,不需要引号

[ \( a = a -o a = b \) -a a = b ]:语法错误,扩展为()


{ [ a = a ] || [ a = b ]; } && [ a = b ]:语法如果当前目录中有多个文件,则会发生错误。

x='a b'; [[ $x = 'a b' ]]:等效于POSIX的



x='a b'; [ $x = 'a b' ]



[ a b = 'a b' ]:正确,因为它可以进行模式匹配(x='*'; [ $x = 'a b' ]很神奇)。不会将glob扩展到当前目录中的文件。

x='a b'; [ "$x" = 'a b' ]= glob会扩展。因此,根据当前目录中的文件,可能是true还是false。

[[ ab = a? ]]:false,不是全局扩展但是* ? [是Bash扩展名。



[ ab = a? ]



a?:正确,POSIX扩展正则表达式匹配,[ ab = a\? ]不全局扩展

=:语法错误。没有bash等效项。

==:等效于POSIX(仅单行数据)

[:等效于POSIX。 >建议:请始终使用[[

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

如果使用case ab in (a?) echo match; esac,则:


可移植性差
迫使读者学习另一个bash扩展的复杂性。 [[ ab =~ 'ab?' ]]只是一个具有奇怪名称的常规命令,不涉及任何特殊的语义。


¹源自Korn shell中等效的''构造

²,但对于[[ ab? =~ 'ab?' ]]=~的某些值(例如[[ ab =~ ab? ]]?)失败,并且如果[ a =~ a ]printf 'ab\n' | grep -Eq 'ab?'看起来像十进制整数,则进行数值比较。 awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?'可以同时解决这两个问题。

³,并且对于某些[][[ ]]值(如[[ ]][)也失败。没有启用(例如[[...]]

5,尽管不需要分组(这里使用a命令组而不是b,它将运行不必要的子shell),因为+index shell运算符(相对于abexpr "x$a" '<' "x$b"运算符或a / b!运算符)具有相同的优先级。因此,(等效。

评论


@StéphaneChazelas感谢您的信息!我已经将expr添加到了答案。术语“ Bash扩展”并不意味着Bash是第一个添加一些语法的Shell,学习POSIX sh vs Bash已经足以使我发疯。

– Ciro Santilli郝海东冠状病六四事件法轮功
19年2月5日,9:11

如果尝试过男人[并且迷路了,请参见man test。这将解释POSIX变体。

–乔纳森·科玛(Jonathan Komar)
19年4月4日在7:25

#4 楼

从bash文档中:(list)列表在子shell环境中执行(请参阅下面的COMMAND EXECUTION ENVIRONMENT)。在命令完成后,影响外壳环境的变量分配和内置命令将保持无效。返回状态是list的退出状态。


换句话说,您要确保'list'中发生的任何事情(例如cd)在()之外均无效。唯一会泄漏的是最后一个命令的退出代码,或者与set -e一起生成错误的第一个命令(除了少数几个,例如ifwhile等)。


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


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


[[ expression ]]根据评估返回状态0或1条件表达式的表达式。表达式由以下条件表达式中描述的基元组成。不对[[和]]之间的单词进行单词拆分和路径名扩展;执行波浪号扩展,参数和变量扩展,算术扩展,命令替换,进程替换和引用删除。条件运算符(例如-f)必须不加引号,才能将其识别为基数。 br />此功能提供了高级测试,可以比较字符串,数字和文件,类似于test所提供的功能,但功能更强大。


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

[...]


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

请注意,在bash的情况下,[test是内置函数(如注释中所述),但是存在几乎相同的限制。

评论


尽管test和[当然是Bash中的内置命令,但是很可能也存在一个外部二进制文件。

–ilkkachu
16年8月27日在19:12

[的外部二进制文件不是在大多数现代系统上进行测试的符号链接。

–Random832
16年8月27日在19:16



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

–ilkkachu
16年8月27日在19:50

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

– jlliagre
16-8-28的4:00



(出口1)在括号之外有作用。

–亚历山大
18年10月10日在8:23

#5 楼

某些示例:

传统测试:

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 ...


#6 楼

将命令分组

Bash提供了两种将要作为单元执行的命令列表分组的方法。

( list )将命令列表放在括号之间会导致创建子外壳环境,以及列表中要在该子Shell中执行的每个命令。由于该列表是在子Shell中执行的,因此变量赋值在子Shell完成后不会保持有效。
。没有创建子外壳。以下列表是分号(或换行符)。来源
$ a=1; (a=2; echo "inside: a=$a"); echo "outside: a=$a"
inside: a=2
outside: a=1


条件构造
单括号,即{ list; }
为了比较[]==, !=, <,,应使用>eq, ne,lt进行数值比较。
增强的括号,即gt
在以上所有示例中,我们仅使用单括号将条件表达式括起来,但是bash允许使用双括号,这是单括号语法的增强版本。
对于比较[[]]==, !=, <,可以直接使用。


>是test命令的同义词。即使将其内置到外壳中,它也会创建一个新进程。

[是它的新改进版本,它是关键字,而不是程序。

[[[[Korn理解。

来源