一位同事最近在代码审查中声称,该[[ ]]
构造[ ]
在构造中比
if [ "`id -nu`" = "$someuser" ] ; then
echo "I love you madly, $someuser"
fi
他无法提供理由。有吗?
一位同事最近在代码审查中声称,该[[ ]]
构造[ ]
在构造中比
if [ "`id -nu`" = "$someuser" ] ; then
echo "I love you madly, $someuser"
fi
他无法提供理由。有吗?
[[
惊喜较少,通常使用起来更安全。但它不是可移植的——POSIX 没有指定它的作用,只有一些 shell 支持它(除了 bash,我听说 ksh 也支持它)。例如,你可以做
[[ -e $b ]]
测试文件是否存在。但是对于[
,您必须引用$b
,因为它会拆分论点并扩展诸如"a*"
([[
字面意思是)之类的内容。这也与如何[
成为外部程序并像其他所有程序一样正常接收其参数有关(尽管它也可以是内置程序,但它仍然没有这种特殊处理)。
[[
还有一些其他不错的功能,例如正则表达式匹配=~
以及类似 C 语言中已知的运算符。这是一个很好的页面:测试和测试有什么区别?[
[[
和Bash 测试
行为差异
Bash 4.3.11 的一些区别:
POSIX 与 Bash 扩展:
[
是 POSIX[[
是一个受KornShell启发的 Bash 扩展常规命令与魔法
[
只是一个带有奇怪名称的常规命令。
]
只是 的最后一个参数[
。
Ubuntu 16.04实际上有一个/usr/bin/[
由coreutils提供的可执行文件,但 Bash 内置版本优先。
Bash 解析命令的方式没有任何改变。
特别是,<
是重定向,&&
并||
连接多个命令,( )
生成子shell,除非转义\
,并且单词扩展照常发生。
[[ X ]]
是一个X
可以神奇地解析的单一结构。<
, &&
,||
和()
被特殊对待,分词规则不同。
还有更多的区别,如=
和=~
。
在 Bashese: [
is a built-in command, and [[
is a keyword: shell builtin 和 shell 关键字有什么区别?
<
[[ a < b ]]
: 字典比较[ a \< b ]
: 同上。\
required 或者像任何其他命令一样进行重定向。Bash 扩展。expr x"$x" \< x"$y" > /dev/null
或[ "$(expr x"$x" \< x"$y")" = 1 ]
:POSIX 等价物,请参阅:How to test strings for lexicographic less than or equal in Bash?&&
和||
[[ a = a && b = b ]]
: 真实的,合乎逻辑的[ a = a && b = b ]
: 语法错误,&&
解析为 AND 命令分隔符cmd1 && cmd2
[ a = a ] && [ b = b ]
: POSIX 可靠等价物[ a = a -a b = b ]
a
: 几乎等价,但被 POSIX 弃用,因为它是疯狂的,并且对于or b
like !
or的某些值会失败,或者(
将被解释为逻辑操作(
[[ (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 ]
,因为||
和&&
shell 运算符具有相同的优先级,不像[[ || ]]
and[[ && ]]
和-o
, -a
and[
扩展时的分词和文件名生成 (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\? ]
: false,不是全局扩展=
and在and==
中是相同的,但是是一个 Bash 扩展。[
[[
==
case ab in (a?) echo match; esac
: POSIX 等价物[[ ab =~ 'ab?' ]]
: false,''
在 Bash 3.2 及更高版本中失去魔力,并且未启用对 Bash 3.1 的兼容性(如BASH_COMPAT=3.1
)[[ ab? =~ 'ab?' ]]
: 真的=~
[[ ab =~ ab? ]]
: 真的。POSIX扩展正则表达式匹配并且?
不全局扩展[ a =~ a ]
: 语法错误。没有 Bash 等价物。printf 'ab\n' | grep -Eq 'ab?'
: POSIX 等效项(仅单行数据)awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?'
: POSIX 等价物。建议:一直使用[]
[[ ]]
我见过的每个构造都有 POSIX 等价物。
如果你使用[[ ]]
你:
[
只是一个名称怪异的常规命令,不涉及特殊语义。感谢Stéphane Chazelas的重要更正和补充。
[[ ]]
有更多的特性——我建议你看一下Advanced Bash Scripting Guide以获得更多信息,特别是第 7 章中的扩展测试命令部分。测试。
顺便说一下,正如指南所指出的,[[ ]]
它是在 ksh88(KornShell 的 1988 版本)中引入的。
双括号是一个“复合命令”,其中 test 和单括号是 shell 内置的(实际上是相同的命令)。因此,单括号和双括号执行不同的代码。
测试和单括号是最便携的,因为它们作为单独的外部命令存在。但是,如果您使用任何远程现代版本的 BASH,则支持双括号。
如果您喜欢遵循Google 的风格指南:
测试
[ … ]
, 和[[ … ]]
[[ … ]]
优于[ … ]
,test
和/usr/bin/[
.
[[ … ]]
[[
减少了错误,因为和之间没有路径名扩展或分词]]
。此外,[[ … ]]
允许正则表达式匹配,而[ … ]
不允许。# 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. 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
在标题中明确包含“in Bash”的标记为“bash”的问题中,我对所有回复说你应该避免[[
......]]
因为它只在 bash 中有效!
确实,可移植性是主要的反对意见:如果你想编写一个在 Bourne 兼容的 shell 中工作的 shell 脚本,即使它们不是 bash,你应该避免[[
...。]]
(如果你想在更严格的 POSIX shell 中测试你的 shell 脚本,我建议dash
;虽然它是一个不完整的 POSIX 实现,因为它缺乏标准所需的国际化支持,但它也缺乏对发现的许多非 POSIX 构造的支持在 bash、ksh、zsh 等中)
我看到的另一个反对意见至少适用于 bash 的假设:那[[
...]]
有它自己的特殊规则,你必须学习,而[
...]
就像另一个命令一样。这又是真的(桑蒂利先生带来了显示所有差异的收据),但差异是好是坏是相当主观的。我个人发现,双括号结构让我可以使用(
...)
进行分组、&&
布尔||
逻辑、<
比较>
和不带引号的参数扩展。它就像它自己的小封闭世界,表达式的工作方式更像是在传统的非命令 shell 编程语言中。
我没有看到的一点是 ... 的这种行为[[
与算术扩展构造...的行为]]
完全一致,它由POSIX 指定,并且还允许不带引号的括号以及布尔和不等式运算符(在此处执行数字而不是词法比较)。本质上,每当您看到双括号字符时,您都会获得相同的引号屏蔽效果。$((
))
(Bash 和它的现代亲戚也使用((
... ))
- 没有前导$
- 作为 C 风格的for
循环头或执行算术运算的环境;这两种语法都不是 POSIX 的一部分。)
所以有一些很好的理由更喜欢[[
...... ]]
;也有理由避免它,这可能适用于您的环境,也可能不适用。至于你的同事,“我们的风格指南这么说”是一个有效的理由,就它而言,但我也会从理解风格指南为什么推荐它的人那里寻找背景故事。
您无法使用的典型情况[[
是在 autotools configure.ac 脚本中。括号有一个特殊的和不同的含义,所以你将不得不使用test
代替[
或[[
-- 注意 test 和[
是同一个程序。
[[ ]] 双括号在某些版本的SunOS下不受支持,并且在函数声明中完全不受支持:
GNU Bash,版本 2.02.0(1)-release (sparc-sun-solaris2.6)
简而言之, [[ 更好,因为它不会分叉另一个进程。没有括号或单括号比双括号慢,因为它分叉了另一个进程。