我只是想知道
[[ $STRING != foo ]]
和
[ $STRING != foo ]
是,除此之外后者是符合 POSIX 标准的,可以在 sh 中找到,而前者是 bash 中的扩展。
答案1
有几个不同之处。在我看来,最重要的几点是:
[
是 Bash 和许多其他现代 shell 中的内置命令。内置命令[
类似于,但test
需要以 结尾]
。内置命令[
和test
模仿功能/bin/[
和/bin/test
及其限制,以便脚本可以向后兼容。原始可执行文件仍然存在,主要是为了符合 POSIX 并向后兼容。type [
在 Bash 中运行该命令表示[
默认情况下将其解释为内置命令。(注意:which [
仅在小路并且相当于type -P [
。你可以执行type --help
以了解详细信息)[[
兼容性不高,因此不一定能与任何/bin/sh
指向的兼容。[[
更现代的 Bash / Zsh / Ksh 选项也是如此。- 由于
[[
内置于 shell 中,并且没有遗留要求,因此您无需担心基于独立财务顾问变量会弄乱求值为带空格的字符串的变量。因此,您实际上不需要将变量放在双引号中。
在大多数情况下,其余的只是一些更好的语法。要查看更多差异,我建议点击此链接查看常见问题解答:测试、[ 和 [[ 之间有什么区别?。事实上,如果你对 Bash 脚本很感兴趣,我建议你阅读整个维基百科,包括常见问题解答,陷阱、和指南。 测试部分来自指南部分也解释了这些差异,以及作者为什么认为[[
如果您不需要担心便携性,这是一个更好的选择。主要原因是:
- 您不必担心引用测试的左侧,以便它实际上被读取为变量。
- 您不必使用
< >
反斜杠转义小于和大于,以免它们被评估为输入重定向,这可能会通过覆盖文件而弄乱一些东西。这又回到了[[
内置状态。如果 [ (test) 是一个外部程序,shell 必须在评估方式上做出例外<
,并且>
只有在/bin/test
被调用时才会这样做,这实际上没有意义。
答案2
简而言之:
[ 是 bash内置
[[ ]] 是 bash关键词
关键字:关键字与内置命令非常相似,但主要区别在于它们适用特殊的解析规则。例如,[ 是 bash 内置命令,而 [[ 是 bash 关键字。它们都用于测试内容,但由于 [[ 是关键字而不是内置命令,因此它受益于一些特殊的解析规则,这使其变得容易得多:
$ [ a < b ]
-bash: b: No such file or directory
$ [[ a < b ]]
第一个例子返回错误,因为 bash 尝试将文件 b 重定向到命令 [ a ]。第二个例子实际上做了你期望的事情。字符 < 不再具有文件重定向运算符的特殊含义。
来源:http://mywiki.wooledge.org/BashGuide/CommandsAndArguments
答案3
行为差异
Bash 4.3.11 上的一些差异:
POSIX 与 Bash 扩展:
[
是 POSIX[[
是一个 Bash 扩展灵感来自 Korn shell
常规命令与魔法
[
只是一个具有奇怪名称的常规命令。]
只是 的最后一个参数[
。
Ubuntu 16.04 实际上有一个可执行文件,
/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 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 ]]
:false。如果没有( )
,则为真,因为[[ && ]]
优先级高于[[ || ]]
[ ( 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 运算符具有相等的优先级,不同于[[ || ]]
和[[ && ]]
和-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? ]]
:是的,因为确实如此模式匹配(* ? [
是魔法)。不会将文件全局扩展至当前目录中。[ 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 的兼容性(如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 等效物。
如果您使用[[ ]]
:
- 丧失可移植性
- 迫使读者学习另一个 bash 扩展的复杂性。
[
只是一个具有奇怪名称的常规命令,不涉及任何特殊语义。
谢谢斯蒂芬·查泽拉斯进行重要的更正和补充。
答案4
根据对手册页相关部分的快速阅读,主要区别似乎在于==
and!=
运算符与模式匹配,而不是与文字字符串匹配,并且还有=~
正则表达式比较运算符。