ChatGPT解决这个技术问题 Extra ChatGPT

Bash中单方括号和双方括号的区别

我正在阅读有关 if 的 bash 示例,但有些示例是用单方括号编写的:

if [ -f $param ]
then
  #...
fi

其他带双方括号的:

if [[ $? -ne 0 ]]
then
    start looking for errors in yourlog
fi

有什么区别?

您可以通过查看以下问题的答案来获得答案:unix.stackexchange.com/questions/3831/…

c
cmh

单个 [] 是符合 posix shell 的条件测试。

[[]] 是对标准 [] 的扩展,并且受到 bash 和其他 shell(例如 zsh、ksh)的支持。它们支持额外的操作(以及标准的 posix 操作)。例如:|| 而不是 -o 以及与 =~ 匹配的正则表达式。可以在 bash manual section on conditional constructs 中找到更完整的差异列表。

每当您希望脚本可跨 shell 移植时,请使用 []。如果您想要 [] 不支持且不需要可移植的条件表达式,请使用 [[]]


我要补充一点,如果您的脚本不是以明确请求支持 [[ ]] 的 shell(例如带有 #!/bin/bash#!/usr/bin/env bash 的 bash)的 shebang 开头,您应该使用可移植选项。假设 /bin/sh 支持此类扩展的脚本将在操作系统(如最近的 Debian 和 Ubuntu 版本)上中断,但情况并非如此。
C
Ciro Santilli Путлер Капут 六四事

行为差异

在 Bash 4.3.11 中测试:

POSIX 与 Bash 扩展:[ 是 POSIX [[ 是受 Korn shell 启发的 Bash 扩展

是 POSIX

[[ 是一个受 Korn shell 启发的 Bash 扩展

常规命令与魔术 [ 只是一个名称奇怪的常规命令。 ] 只是 [ 的最后一个参数。 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

[ 只是一个带有奇怪名称的常规命令。 ] 只是 [ 的最后一个参数。

[[ X ]] 是一个使 X 被神奇地解析的单一结构。 <, &&, ||和 () 被特殊对待,分词规则不同。还有其他区别,例如 = 和 =~。

< [[ a < b ]]:字典比较 [ a \< b ]:同上。 \ 需要,否则会像任何其他命令一样进行重定向。 Bash 扩展。 expr x"$x" \< x"$y" > /dev/null 或 ["$(expr x"$x" \< x"$y")" = 1 ]:POSIX 等价物,请参阅:如何测试Bash中字典小于或等于的字符串?

[[ a < b ]]:字典比较

a \< b ]:同上。 \ 需要,否则会像任何其他命令一样进行重定向。 Bash 扩展。

expr x"$x" \< x"$y" > /dev/null 或 ["$(expr x"$x" \< x"$y")" = 1 ]:POSIX 等价物,请参阅:如何测试Bash中字典小于或等于的字符串?

&& 和 || [[ a = a && b = b ]]: true, logical and [ a = a && b = b ]: 语法错误,&& 被解析为 AND 命令分隔符 cmd1 && cmd2 [ a = a ] && [ b = b ] : POSIX 可靠等价物 [ a = a -ab = b ]:几乎等价,但被 POSIX 弃用,因为它是疯狂的,并且对于 a 或 b 的某些值失败,例如!或 ( 将被解释为逻辑运算

[[ a = a && b = b ]]:真实,逻辑和

a = a && b = b ]:语法错误,&& 被解析为 AND 命令分隔符 cmd1 && cmd2

[ a = a ] && [ b = b ]:POSIX 可靠等价物

a = a -ab = b ]:几乎等效,但被 POSIX 弃用,因为它很疯狂,并且对于 a 或 b 的某些值(例如!或 ( 将被解释为逻辑运算

[[ (a = a || a = b) && a = b ]]: false。如果没有 ( ),则为真,因为 [[ && ]] 的优先级高于 [[ || ]] [ ( a = a ) ]:语法错误,() 被解释为子 shell [ \( a = a -oa = b \) -aa = b ]:等效,但 ()、-a 和 -o 被 POSIX 弃用。没有 \ ( \) 将是正确的,因为 -a 的优先级高于 -o { [ a = a ] || [ a = b ]; } && [ a = b ] 不推荐使用的 POSIX 等效项。然而,在这种特殊情况下,我们可以刚刚写了: [ a = a ] || [ a = b ] && [ a = b ] 因为 || 和 && shell 运算符具有相同的优先级,不像 [[ || ]] 和 [[ && ]] 和 -o, -a 和 [

[[ (a = a || a = b) && a = b ]]: 错误。如果没有 ( ),则为真,因为 [[ && ]] 的优先级高于 [[ || ]]

[ ( a = a ) ]: 语法错误, () 被解释为子shell

\( a = a -oa = b \) -aa = b ]:等效,但 ()、-a 和 -o 已被 POSIX 弃用。没有 \( \) 将是真的,因为 -a 比 -o 具有更高的优先级

[ 一 = 一 ] || [一=乙]; } && [ a = b ] 不推荐使用的 POSIX 等效项。然而,在这种特殊情况下,我们可以只写: [ a = a ] || [ a = b ] && [ a = b ] 因为 ||和 && shell 运算符具有相同的优先级,不像 [[ || ]] 和 [[ && ]] 和 -o、-a 和 [

扩展时的分词和文件名生成 (split+glob) x='a b'; [[ $x = 'a b' ]]: true, 不需要引号 x='a b'; [ $x = 'a b' ]: 语法错误,展开为 [ ab = 'a b' ] x='*'; [ $x = 'a b' ]:如果当前目录中有多个文件,则语法错误。 x='a b'; [ "$x" = 'a b' ]: POSIX 等价物

x='a b'; [[ $x = 'a b' ]]: true,不需要引号

x='a b'; [ $x = 'a b' ]:语法错误,扩展为 [ ab = 'a b' ]

x='*'; [ $x = 'a b' ]:如果当前目录中有多个文件,则语法错误。

x='a b'; [ "$x" = 'a b' ]: POSIX 等价物

[[ ab = a? ]]: true,因为它会进行模式匹配(* ? [ 是魔法)。不 glob 扩展到当前目录中的文件。 [ ab = a? ]: 一个?球体扩大。所以可能是真或假,具体取决于当前目录中的文件。 [ ab = a\? ]: false,不是全局扩展 = 和 == 在 [ 和 [[ 中是相同的,但 == 是 Bash 扩展。 (a?) 回显匹配中的 case ab; esac: POSIX 等价物 [[ ab =~ 'ab?' ]]: false,在 Bash 3.2 及更高版本中使用 '' 失去魔力,并且未启用对 bash 3.1 的兼容性(如 BASH_COMPAT=3.1)[[ ab? =〜'ab? ]]: 真的

[[ ab = a? ]]: true,因为它会进行模式匹配(* ? [ 是魔法)。不 glob 扩展到当前目录中的文件。

ab = a? ]: 一个?球体扩大。所以可能是真或假,具体取决于当前目录中的文件。

[ ab = a\? ]: false,不是全局扩展

和 == 在 [ 和 [[ 中是相同的,但 == 是 Bash 扩展。

(a?) 回显匹配中的 case ab; esac:POSIX 等价物

[[ ab =~'ab?' ]]: false,在 Bash 3.2 及更高版本中使用 '' 失去魔力,并且未启用对 bash 3.1 的兼容性(如 BASH_COMPAT=3.1)

[[ab? =〜'ab? ]]: 真的

=~ [[ ab =~ ab? ]]: true,POSIX 扩展正则表达式匹配,?不全局展开 [ a =~ a ]:语法错误。没有 bash 等价物。 printf 'ab\n' | grep -Eq 'ab?':POSIX 等效项(仅单行数据) awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?':POSIX 等效项。

[[ ab =~ ab? ]]: true,POSIX 扩展正则表达式匹配,?不全局扩展

a =~ a ]:语法错误。没有 bash 等价物。

printf 'ab\n' | grep -Eq 'ab?':POSIX 等效项(仅单行数据)

awk 'BEGIN{exit !(ARGV[1] ~ ARGV[2])}' ab 'ab?':POSIX 等效项。

建议:始终使用 []

我见过的每个 [[ ]] 构造都有 POSIX 等效项。

如果您使用 [[ ]],您:

失去便携性

迫使读者了解另一个 bash 扩展的复杂性。 [ 只是一个名称怪异的常规命令,不涉及特殊语义。

感谢 Stéphane Chazelas 的重要更正和补充。


我见过的每个 [[ ]] 构造都有 POSIX 等效项。” 对于地球表面上的任何 Turing Complete 语言都可以这样说。
@A.Rick 这将是所有“如何用语言 Y 做 X”SO 问题的有效答案 :-) 当然,该声明中隐含了“方便”。
很棒的总结。感谢您的努力。然而,我不同意该建议。它的可移植性与更一致和更强大的语法。如果您可以在您的环境中要求 bash >4,则建议使用 [[ ]] 语法。
@Downvoters 请解释一下,以便我可以学习和改进信息:-)
很好的答案,但我认为 Recommendation: always use [] 应理解为 My preference :如果您不想失去可移植性,请使用 []。如 here 所述:如果考虑到 POSIX 或 BourneShell 的可移植性/一致性,则应使用旧语法。另一方面,如果脚本需要 BASH、Zsh 或 KornShell,则新语法通常更灵活,但不一定向后兼容。 如果可以,我宁愿使用 [[ ab =~ ab? ]],而且不用担心比 printf 'ab' | grep -Eq 'ab?' 向后兼容
s
sampson-chen

在用于条件测试的单括号内(即 [ ... ]),所有 shell 都支持某些运算符,例如单个 =,而某些较旧的 shell 不支持使用运算符 ==

在条件测试的双括号内(即 [[ ... ]]),在新旧 shell 中使用 === 没有区别。

编辑:我还应该注意:在 bash 中,如果可能,请始终使用双括号 [[ ... ]],因为它比单括号更安全。我将通过以下示例说明原因:

if [ $var == "hello" ]; then

如果 $var 恰好为 null / 空,那么这就是脚本所看到的:

if [ == "hello" ]; then

这会破坏你的脚本。解决方案是使用双括号,或者始终记住在变量周围加上引号 ("$var")。双括号是更好的防御性编码实践。


除非您有充分的理由不这样做,否则在所有变量读取周围加上引号是一种更好的防御性编码实践,因为它适用于所有变量读取,而不仅仅是条件中的那些。如果硬盘驱动器名称包含空格(或类似的东西),iTunes 安装程序错误会删除人们的文件。它也解决了你提到的问题。
c
codeforester

[[ 是一个 bash 关键字,类似于(但比)[ 命令更强大。

http://mywiki.wooledge.org/BashFAQ/031http://mywiki.wooledge.org/BashGuide/TestsAndConditionals

除非您正在编写 POSIX sh,否则我们建议您使用 [[


C
Cromax

[ 是类似 printf 的内置函数。 Bash 语法期望在与命令相同的位置看到它。并且 ] 对 Bash 来说没什么,只是它是 [ 内置函数所期望的。 (man bash / SHELL BUILTIN 命令)

[[ 是一个类似 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 兼容性,则意味着您不是,所以不要关心获得“更多”兼容的脚本不是。


a
asf107

您可以使用方括号进行轻度正则表达式匹配,例如:

if [[ $1 =~ "foo.*bar" ]] ; then

(只要您使用的 bash 版本支持此语法)


除非您引用了该模式,因此它现在被视为文字字符串。
非常真实。有时这让我很烦:)
u
user5500105

Bash manual 说:

当与 [[ 一起使用时,'<' 和 '>' 运算符使用当前语言环境按字典顺序排序。测试命令使用 ASCII 排序。

(测试命令等同于 [ ] )