我正在阅读关于if
的bash示例,但有些示例是用单个方括号编写的:
if [ -f $param ]
then
#...
fi
其他双方括号:
if [[ $? -ne 0 ]]
then
start looking for errors in yourlog
fi
有什么区别?
答案 0 :(得分:156)
单[]
符合posix shell条件测试。
Double [[]]
是标准[]
的扩展,受bash和其他shell支持(例如zsh,ksh)。它们支持额外的操作(以及标准的posix操作)。例如:||
代替-o
,正则表达式与=~
匹配。可以在bash manual section on conditional constructs中找到更完整的差异列表。
只要您希望脚本可以跨shell移植,请使用[]
。如果您希望[[]]
不支持条件表达式并且不需要可移植,请使用[]
。
答案 1 :(得分:68)
行为差异
在Bash 4.3.11中测试:
POSIX vs Bash扩展名:
常规命令vs魔法
[
只是一个带有奇怪名称的常规命令。
]
只是[
的一个参数,可以防止使用其他参数。
Ubuntu 16.04在coreutils提供的/usr/bin/[
实际上有一个可执行文件,但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 a \< b > /dev/null
:POSIX等效项²,请参阅:How to test strings for lexicographic less than or equal in Bash? &&
和||
[[ a = a && b = b ]]
:true,逻辑和 [ a = a && b = b ]
:语法错误,&&
被解析为AND命令分隔符cmd1 && cmd2
[ a = a -a b = b ]
:等效,但已由POSIX³弃用[ a = a ] && [ b = b ]
:POSIX和可靠的等价物 (
[[ (a = a || a = b) && a = b ]]
:false [ ( 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' ]]
:true,引号不需要x='a b'; [ $x = 'a b' ]
:语法错误,展开到[ a b = 'a b' ]
x='*'; [ $x = 'a b' ]
:如果当前目录中有多个文件,则语法错误。x='a b'; [ "$x" = 'a b' ]
:POSIX等效 =
[[ ab = a? ]]
:是的,因为pattern matching(* ? [
是神奇的)。不会将glob扩展为当前目录中的文件。[ ab = a? ]
:a?
glob扩展。因此可能是真或假,具体取决于当前目录中的文件。[ ab = a\? ]
:false,不是全局展开=
和==
在[
和[[
中都相同,但==
是Bash扩展名。case ab in (a?) echo match; esac
:POSIX等效[[ ab =~ 'ab?' ]]
:false 4 ,与''
[[ ab? =~ 'ab?' ]]
:true =~
[[ ab =~ ab? ]]
:true,POSIX extended regular expression匹配,?
不会全局展开[ a =~ a ]
:语法错误。没有bash等价物。printf 'ab\n' | grep -Eq 'ab?'
:POSIX等效项(仅限单行数据)awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?'
:POSIX等效。 推荐:始终使用[]
。
我见过的每个[[ ]]
构造都有POSIX等价物。
如果你使用[[ ]]
:
[
只是一个带有奇怪名称的常规命令,不涉及特殊的语义。 ¹灵感来自Korn shell中等效的[[...]]
构造
²但对a
或b
(如+
或index
)的某些值失败,并且如果a
和{进行数字比较{1}}看起来像十进制整数。 b
适用于两者。
³并且expr "x$a" '<' "x$b"
或a
的某些值(例如b
或!
也失败了。
4 并且未启用与bash 3.1的兼容性(与(
一样)
5 虽然分组(这里使用BASH_COMPAT=3.1
命令组而不是{...;}
运行不必要的子shell)不是必需的{{1 }}和(...)
shell运算符(而不是||
和&&
||
运算符或&&
/ [[...]]
-o
运营商)具有相同的优先权。所以-a
是等价的。
答案 2 :(得分:13)
在条件测试的单个括号内(即[...]),所有shell都支持某些运算符,如单=
,而某些运算符不支持使用运算符==
。旧壳。
用于条件测试的双括号内(即[[...]]),在旧壳或新壳中使用=
或==
之间没有区别。
编辑:我还应该注意:在bash中,如果可能的话,总是使用双括号[[...]],因为它比单个括号更安全。我将通过以下示例说明原因:
if [ $var == "hello" ]; then
如果$ var恰好为null / empty,那么这就是脚本看到的内容:
if [ == "hello" ]; then
会破坏你的脚本。解决方案是使用双括号,或者始终记住在变量周围加上引号("$var"
)。双括号是更好的防御性编码实践。
答案 3 :(得分:10)
[[
是一个类似于[
命令(但功能更强大)的bash关键字。
见
http://mywiki.wooledge.org/BashFAQ/031和http://mywiki.wooledge.org/BashGuide/TestsAndConditionals
除非您是为POSIX sh撰写的,否则我们建议使用[[
。
答案 4 :(得分:1)
您可以使用双方括号进行轻型正则表达式匹配,例如:
if [[ $1 =~ "foo.*bar" ]] ; then
(只要您使用的bash版本支持此语法)
答案 5 :(得分:1)
答案 6 :(得分:0)
[
是一个类似于 printf 的 builtin
。 Bash 语法期望在与命令相同的位置看到它。并且 ]
对 Bash 没有任何意义,只是 [
内置函数期望它。 (man bash / SHELL 内置命令)[[
是一个 keyword
就像 if。 Bash 语法开始也期望它与命令在同一位置,但它不是执行它,而是进入条件上下文。而 ]]
也是结束此上下文的关键字。 (man bash / SHELL GRAMMAR / 复合命令)按顺序,bash 尝试解析:语法关键字 > 用户别名 > 内置函数 > 用户函数 > $PATH 中的命令
type [ # [ is a shell builtin
type [[ # [[ is a shell keyword
type ] # bash: type: ]: not found
type ]] # ]] is a shell keyword
compgen -k # Keywords: if then else ...
compgen -b # Builtins: . : [ alias bg bind ...
which [ # /usr/bin/[
[
速度较慢 <= 我猜它会执行更多的解析代码。但我知道它调用了相同数量的系统调用(用[[
在语法上更容易解析,即使是人类,因为它启动了一个上下文。对于算术条件,请考虑使用 ((
。time for i in {1..1000000}; do [ 'a' = 'b' ] ; done # 1.990s
time for i in {1..1000000}; do [[ 'a' == 'b' ]] ; done # 1.371s
time for i in {1..1000000}; do if [ 'a' = 'a' ]; then if [ 'a' = 'b' ];then :; fi; fi ; done # 3.512s
time for i in {1..1000000}; do if [[ 'a' == 'a' ]]; then if [[ 'a' == 'b' ]];then :; fi; fi; done # 2.143s
strace -cf bash -c "for i in {1..100000}; do if [ 'a' = 'a' ]; then if [ 'a' = 'b' ];then :; fi; fi ; done;" # 399
strace -cf bash -c "for i in {1..100000}; do if [[ 'a' == 'a' ]]; then if [[ 'a' == 'b' ]];then :; fi; fi ; done;" # 399
我建议使用 [[
:如果您不明确关心 posix 兼容性,则意味着您不是,所以不要在意让不兼容的脚本“更多”兼容。