我对这些运算符在 bash 中使用时有何不同(括号、双括号、括号和双括号)感到有些困惑。
[[ , [ , ( , ((
Run Code Online (Sandbox Code Playgroud)
我见过人们在这样的 if 语句中使用它们:
if [[condition]]
if [condition]
if ((condition))
if (condition)
Run Code Online (Sandbox Code Playgroud)
Joh*_*024 494
在类似 Bourne 的 shell 中,if语句通常看起来像
if
command-list1
then
command-list2
else
command-list3
fi
Run Code Online (Sandbox Code Playgroud)
then如果command-list1命令列表的退出代码为零,则执行该子句。如果退出代码非零,则else执行该子句。 command-list1可以是简单的,也可以是复杂的。它可以,例如,是由操作者的一个分隔的一个或多个管道,一个序列;,&,&&,||或换行符。下面if显示的条件只是 的特殊情况command-list1:
if [ condition ]
[是传统test命令的另一个名称。 [/test是标准的 POSIX 实用程序。所有 POSIX shell 都内置了它(尽管 POSIX² 不需要)。该test命令设置一个退出代码,该if语句会相应地执行操作。典型的测试是文件是否存在或一个数字是否等于另一个。
if [[ condition ]]
这是test来自ksh 的¹的新升级变体,bash、zsh、yash、busybox sh也支持。此[[ ... ]]构造还设置退出代码,if语句会相应地执行操作。在其扩展功能中,它可以测试字符串是否与通配符模式匹配(不在busybox sh 中)。
if ((condition))
bash和zsh也支持的另一个ksh扩展。这将执行算术运算。作为算术的结果,设置退出代码并且语句相应地起作用。如果算术计算的结果非零,则返回零 (true) 的退出代码。就像,这种形式不是 POSIX,因此不可移植。if[[...]]
if (command)
这将在子 shell 中运行命令。当命令完成时,它会设置一个退出代码,并且if语句会相应地执行操作。
用于使用子shell像这样的典型原因是限制的副作用command,如果command需要变量赋值或其他变化到外壳的使用环境。子shell 完成后,此类更改不会保留。
if command
命令被执行,if语句根据其退出代码执行操作。
¹ 虽然不是真正的命令,而是一种特殊的 shell 构造,它具有与普通命令不同的语法,并且在不同的 shell 实现之间存在显着差异
²POSIX并不要求有一个独立的test和[但是在系统上的实用程序,虽然在的情况下[,一些Linux发行版已经知道丢失了。
Gil*_*il' 132
(…)括号表示子shell。它们内部的内容与许多其他语言中的表达方式不同。它是一个命令列表(就像外括号一样)。这些命令在单独的子进程中执行,因此在括号内执行的任何重定向、赋值等在括号外都没有影响。
{ … }大括号就像括号,因为它们对命令进行分组,但它们只影响解析,而不影响分组。程序x=2; { x=4; }; echo $x打印 4,而x=2; (x=4); echo $x打印 2。(同样大括号作为关键字需要分隔并在命令位置找到(因此后面{和;之前的空格})而括号则没有。这只是一个语法怪癖。)
${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 …
Run Code Online (Sandbox Code Playgroud)如果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
Run Code Online (Sandbox Code Playgroud)测试myfile当前目录下是否有调用的文件:
if [ -e myfile ]; then …
Run Code Online (Sandbox Code Playgroud)相同,但也包括悬空的符号链接:
if [ -e myfile ] || [ -L myfile ]; then …
Run Code Online (Sandbox Code Playgroud)测试x(假定为数字)的值是否至少为 2,可移植:
if [ "$x" -ge 2 ]; then …
Run Code Online (Sandbox Code Playgroud)x在 bash/ksh/zsh 中测试(假定为数字)的值是否至少为 2:
if ((x >= 2)); then …
Run Code Online (Sandbox Code Playgroud)Cir*_*郝海东 42
[ 对比 [[
此答案将涵盖问题的[vs[[子集。
Bash 4.3.11 的一些差异:
POSIX 与 Bash 扩展:
常规命令 vs 魔法
[ 只是一个带有奇怪名称的常规命令。
]只是 的最后一个参数[。
Ubuntu 16.04 实际上有一个/usr/bin/[由coreutils提供的可执行文件,但 bash 内置版本优先。
Bash 解析命令的方式没有任何改变。
特别<是重定向,&&并||连接多个命令,( )除非被 转义\,否则生成子shell ,并且单词扩展照常发生。
[[ X ]]是一个单一的构造,X可以神奇地解析。<, &&,||和()被特殊对待,分词规则不同。
还有进一步的差异,例如=和=~。
在 Bashese 中:[是一个内置命令,[[是一个关键字:https : //askubuntu.com/questions/445749/whats-the-difference-between-shell-builtin-and-shell-keyword
<
[[ a < b ]]: 字典比较[ a \< b ]: 和上面一样。\需要或者像任何其他命令一样进行重定向。Bash 扩展。expr x"$x" \< x"$y" > /dev/null或[ "$(expr x"$x" \< x"$y")" = 1 ]:POSIX 等效项,请参阅:https : //stackoverflow.com/questions/21294867/how-to-test-strings-for-lexicographic-less-than-or-equal-in-bash/52707989#52707989&& 和 ||
[[ a = a && b = b ]]: 真实的、合乎逻辑的和[ a = a && b = b ]: 语法错误,&&解析为 AND 命令分隔符cmd1 && cmd2[ a = a ] && [ b = b ]: POSIX 可靠等价物[ a = a -a b = b ]: 几乎等价,但被 POSIX 弃用,因为它很疯狂并且对于某些值a或b类似!或(将被解释为逻辑操作失败(
[[ (a = a || a = b) && a = b ]]: 错误的。没有( ), 将是真的,因为[[ && ]]具有更高的优先级[[ || ]][ ( a = a ) ]: 语法错误,()被解释为子shell[ \( a = a -o a = b \) -a a = b ]: 等效,但是()、-a和-o被 POSIX 弃用。没有\( \)将是真的,因为-a它的优先级高于-o{ [ a = a ] || [ a = b ]; } && [ a = b ]未弃用的 POSIX 等效项。然而,在这种特殊情况下,其实我们可以只写:[ a = a ] || [ a = b ] && [ a = b ]因为||和&&外壳运营商有不同相同的优先级[[ || ]]和[[ && ]]和-o,-a和[扩展时的分词和文件名生成(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 扩展。所以根据当前目录中的文件可能为真或假。[ ab = a\? ]: false,不是全局扩展=而==在双方的同[和[[,不过==是一个bash扩展。case ab in (a?) echo match; esac: POSIX 等价物[[ ab =~ 'ab?' ]]: false,''在 Bash 3.2 及更高版本中失去魔法,并且未启用与 bash 3.1 的兼容性(如 with BASH_COMPAT=3.1)[[ ab? =~ 'ab?' ]]: 真的=~
[[ ab =~ ab? ]]: true,POSIX扩展正则表达式匹配,?不 glob 扩展[ a =~ a ]: 语法错误。没有 bash 等价物。printf 'ab\n' | grep -Eq 'ab?': POSIX 等价物(仅限单行数据)awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?': POSIX 等价物。建议:一直使用 []
[[ ]]我见过的每个构造都有 POSIX 等价物。
如果你使用[[ ]]你:
[只是一个带有奇怪名称的常规命令,不涉及特殊语义。感谢Stéphane Chazelas的重要更正和补充。
Ale*_*lke 24
从bash 文档:
(list)list 在子 shell 环境中执行(请参阅下面的命令执行环境)。影响 shell 环境的变量赋值和内置命令在命令完成后不再有效。返回状态是列表的退出状态。
换句话说,您要确保 'list' 中发生的任何事情(如 a cd)在(and之外没有任何影响)。将泄漏的唯一事情是最后的命令或与退出代码set -e产生一个错误(大于几如其它的第一个命令if,while等等)
((expression))表达式根据以下算术求值中描述的规则进行求值。如果表达式的值非零,则返回状态为 0;否则返回状态为 1。这完全等同于 let "expression"。
这是一个 bash 扩展,允许你做数学。这有点类似于 using exprwithout all of the expr(例如到处都有空格,转义*等)
[[ expression ]]根据条件表达式表达式的计算结果返回 0 或 1 状态。表达式由以下条件表达式中描述的主要元素组成。对[[和]]之间的词不进行分词和路径名扩展;执行波浪号扩展、参数和变量扩展、算术扩展、命令替换、进程替换和引号删除。条件运算符(例如 -f)必须不加引号才能被识别为主要运算符。当与 [[ 一起使用时,< 和 > 运算符使用当前语言环境按字典顺序排序。
这提供了一种高级测试来比较字符串、数字和文件,有点像test优惠,但功能更强大。
[ expr ]根据条件表达式 expr 的计算结果返回 0(真)或 1(假)状态。每个 operator 和 oper and 必须是一个单独的参数。表达式由上述条件表达式中的主要元素组成。test 不接受任何选项,也不接受和忽略 -- 表示选项结束的参数。[...]
这个叫test。实际上,在过去,[是指向test. 它的工作方式相同,您也有相同的限制。由于二进制文件知道其启动时的名称,因此测试程序知道它何时启动,[并且可以忽略其最后一个参数,该参数预计为]. 有趣的 Unix 技巧。
请注意,如果bash,[和test是内置函数(如评论中所述),但几乎适用相同的限制。
ilk*_*chu 14
一些例子:
传统测试:
foo="some thing"
# check if value of foo is not empty
if [ -n "$foo" ] ; then...
if test -n "$foo" ; then...
Run Code Online (Sandbox Code Playgroud)
testand 和[其他命令一样,所以变量被拆分成单词,除非它在引号中。
新式测试
[[ ... ]] 是一个(较新的)特殊 shell 构造,它的工作方式有点不同,最明显的是它不分词变量:
if [[ -n $foo ]] ; then...
Run Code Online (Sandbox Code Playgroud)
算术测试:
foo=12 bar=3
if (( $foo + $bar == 15 )) ; then ...
Run Code Online (Sandbox Code Playgroud)
“正常”命令:
以上所有命令都像普通命令一样,if可以接受任何命令:
# grep returns true if it finds something
if grep pattern file ; then ...
Run Code Online (Sandbox Code Playgroud)
多个命令:
或者我们可以使用多个命令。包装一组命令在( ... )子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 ...
Run Code Online (Sandbox Code Playgroud)
Pre*_*raj 12
特殊字符:
\n| 查尔。 | 描述 |
|---|---|
| 空白 \xe2\x80\x94 这是制表符、换行符、垂直制表符、换页符、回车符或空格。Bash 使用空格来确定单词的开始和结束位置。第一个单词是命令名称,其他单词成为该命令的参数。 |
$ | 扩展 \xe2\x80\x94 引入了各种类型的扩展:参数扩展(例如$var或${var})、命令替换(例如$(command))或算术扩展(例如$((expression)))。 |
\'\' | 单引号 \xe2\x80\x94 保护其中的文本,使其具有字面意义。使用它们,通常 Bash 的任何类型的解释都会被忽略:特殊字符会被忽略,并且多个单词不会被分割。 |
"" | 双引号 \xe2\x80\x94 保护其中的文本不被分割成多个单词或参数,但允许替换发生;大多数其他特殊字符的含义通常被阻止。 |
\\ | 转义 \xe2\x80\x94(反斜杠)可防止下一个字符被解释为特殊字符。这在引号之外、双引号内有效,并且通常在单引号中被忽略。 |
# | Comment \xe2\x80\x94 该#字符开始注释,一直延伸到行尾。注释是解释性的注释,不被 shell 处理。 |
= | 赋值——给变量赋值(例如logdir=/var/log/myprog)。字符两侧不允许有空格=。 |
[[ ]] | 测试 \xe2\x80\x94 条件表达式的计算以确定它是“真”还是“假”。Bash 中使用测试来比较字符串、检查文件是否存在等。 |
! | 否定 \xe2\x80\x94 用于否定或反转测试或退出状态。例如:! grep text file; exit $?。 |
>, >>,< | 重定向 \xe2\x80\x94 将命令的输出或输入重定向到文件。 |
| | 管道 \xe2\x80\x94 将一个命令的输出发送到另一命令的输入。这是一种将命令链接在一起的方法。例子:echo "Hello beautiful." | grep -o beautiful。 |
; | 命令分隔符 \xe2\x80\x94 用于分隔同一行上的多个命令。 |
{ } | 大括号内的内联组 \xe2\x80\x94 命令被视为一个命令。当 Bash 语法只需要一个命令并且不需要一个函数时,使用这些命令很方便。 |
( ) | 子 shell 组 \xe2\x80\x94 与上面类似,但其中的命令在子 shell(新进程)中执行。使用起来很像沙箱,如果命令引起副作用(例如更改变量),它将对当前 shell 没有影响。 |
(( )) | 算术表达式 \xe2\x80\x94 带有算术表达式,+、-、*、/等字符是用于计算的数学运算符。它们可用于变量赋值(例如)(( a = 1 + 4 ))以及测试(例如)if (( a < b ))。 |
$(( )) | 算术展开 \xe2\x80\x94 与上面类似,但表达式被替换为算术求值的结果。例子:echo "The average is $(( (a+b)/2 ))"。 |
*,? | Globs——匹配部分文件名的“通配符”字符(例如ls *.txt)。 |
~ | 主目录 \xe2\x80\x94 波浪号代表主目录。单独使用或后跟 时/,表示当前用户的主目录;否则,必须指定用户名(例如ls ~/Documents; cp ~john/.bashrc。)。 |
& | 后台 - 当在命令末尾使用时,在后台运行该命令(不要等待它完成)。 |
已弃用的特殊字符(可识别,但不推荐):
\n| 查尔。 | 描述 |
|---|---|
` ` | 命令替换 - 使用$( )代替。 |
[ ] | Test - 旧测试命令的别名。常用于 POSIX shell 脚本。缺乏 的许多功能[[ ]]。 |
$[ ] | 算术表达式 - 使用$(( ))代替。 |