一位同事最近在代码审核中声称[[ ]]
构造比
[ ]
更受欢迎
if [ "`id -nu`" = "$someuser" ] ; then
echo "I love you madly, $someuser"
fi
他无法提供理由。有吗?
答案 0 :(得分:511)
[[
减少意外,通常使用起来更安全。但它不可移植 - POSIX没有指定它做什么,只有一些shell支持它(除了bash,我听说ksh也支持它)。例如,你可以做
[[ -e $b ]]
测试文件是否存在。但是对于[
,您必须引用$b
,因为它会拆分参数并扩展"a*"
之类的内容(其中[[
从字面上理解)。这也与[
如何成为外部程序有关,并且通常像其他程序一样接收它的参数(尽管它也可以是内置程序,但它仍然没有这种特殊处理)。
[[
还有一些其他不错的功能,例如与=~
匹配的正则表达式以及类似C语言的运算符。这是一个很好的页面:What is the difference between test, [
and [[
?和Bash Tests
答案 1 :(得分:69)
行为差异
关于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 :(得分:51)
[[ ]]
有更多功能 - 建议您查看Advanced Bash Scripting Guide了解更多信息,特别是extended test command中的 Chapter 7. Tests 部分
顺便提一下,正如指南所说,[[ ]]
是在ksh88(1988年版的Korn shell)中引入的。
答案 3 :(得分:8)
来自Which comparator, test, bracket, or double bracket, is fastest?(http://bashcurescancer.com)
双括号是“复合物 命令“测试和单一的地方 括号是shell内置的(并且在 现实是相同的命令)。从而, 单支架和双支架 执行不同的代码。
测试和单支架是 最便携的,因为它们存在 单独和外部命令。 但是,如果您使用任何远程 现代版BASH,双 支持。
答案 4 :(得分:7)
在标题为“ bash”的标题中明确带有“ in Bash”的问题中,我对所有应避免使用[[
... ]]
的答复感到有些惊讶它只能在bash中使用!
的确,移植性是主要的反对意见:如果您想编写一个在Bourne兼容的shell中也可以工作的shell脚本,即使它们不是bash,也应避免使用[[
... {{1} }。 (并且,如果您想在更严格的POSIX shell中测试您的shell脚本,我建议使用]]
;尽管它是不完整的POSIX实现,因为它缺少标准所要求的国际化支持,但它也缺少对许多支持的支持。在bash,ksh,zsh等中找到非POSIX结构)
我看到的另一个异议至少可以适用于bash假设:dash
... [[
有自己的特殊规则,而]]
则需要学习。 .. [
就像另一个命令一样。再次是对的(Santilli先生带来了显示所有差异的收据),但是差异的好坏是相当主观的。我个人发现双括号结构使我可以使用]
... (
进行分组,)
和&&
进行布尔逻辑,||
和<
用于比较,以及未引用的参数扩展。就像它自己的封闭世界一样,表达式的工作方式就像在传统的非命令外壳编程语言中一样。
我没有看到的一点是,>
... [[
的这种行为与算术扩展构造]]
... {{1 }},这是POSIX指定的 ,并且还允许使用无引号的括号以及布尔值和不等式运算符(此处执行数字运算而不是词法比较)。本质上,每当您看到双括号字符时,您都会获得相同的引用屏蔽效果。
(Bash及其现代亲戚也使用$((
... ))
–不使用前导((
–作为C样式))
循环标头或环境用于执行算术运算;两种语法都不是POSIX的一部分。)
因此,有一些充分的理由偏爱$
... for
;还有避免它的原因,这可能适用于您的环境,也可能不适用。对于您的同事而言,“我们的风格指南如此说”是一个合理的证明,但就我所能理解的人而言,我还会从了解该风格指南为何推荐其用途的人那里寻找背景故事。
答案 5 :(得分:1)
一种典型的情况,你不能使用[[在autotools configure.ac脚本中,括号有一个特殊和不同的含义,所以你必须使用test
而不是[或[[ - 注意]那个测试和[是同一个程序。
答案 6 :(得分:1)
如果您关注以下Google's style guide:
测试,[和[[
[[...]]减少了错误,因为在[[和]]之间没有路径名扩展或单词拆分,并且[[...]]允许在[...]不存在的情况下进行正则表达式匹配。 / p>
# This ensures the string on the left is made up of characters in the
# alnum character class followed by the string name.
# Note that the RHS should not be quoted here.
# For the gory details, see
# E14 at https://tiswww.case.edu/php/chet/bash/FAQ
if [[ "filename" =~ ^[[:alnum:]]+name ]]; then
echo "Match"
fi
# This matches the exact pattern "f*" (Does not match in this case)
if [[ "filename" == "f*" ]]; then
echo "Match"
fi
# This gives a "too many arguments" error as f* is expanded to the
# contents of the current directory
if [ "filename" == f* ]; then
echo "Match"
fi
答案 7 :(得分:-1)
简而言之,[[更好,因为它不会分叉另一个进程。没有括号或单个括号比双括号慢,因为它会产生另一个过程。
答案 8 :(得分:-1)
[[]]双括号在某些版本的SunOS下不受支持,并且完全不支持内部函数声明: GNU bash,版本2.02.0(1)-release(sparc-sun-solaris2.6)