今天我在编写一些 bash 脚本时发现了一些令人惊讶的事情。我已将其归结为这个最小的示例。
[[ a>b ]]; echo $?
根据我的理解,因为 周围没有空格>
,所以这应该测试字符串是否a>b
不为空,并且应该返回错误代码0
。然而,上面的命令1
在我测试的两个 bash 版本中都有回显(详细信息如下)。
我还使用旧的“good”test
命令进行了测试,
[ a>b ]; echo $?
echo 0
(测试字符串a
不为空并b
在我当前的工作目录中创建一个空文件,显然>b
被视为重定向,这是可以理解的)。
然后我又尝试了一些其他的事情
[[ b>a ]]; echo $?
回声0
并且没有创建文件。[[ b<a ]]; echo $?
回声1
并且没有创建文件。[[ a<b ]]; echo $?
回声0
并且没有创建文件。[ b>a ]; echo $?
echo0
并创建一个空文件a
。[ b<a ]; echo $?
报告丢失文件的错误a
并1
由于该错误而回显。[ a<b ]; echo $?
报告丢失文件的错误b
并1
由于该错误而回显。[[ a=b ]]; echo $?
echos0
因为它正在测试字符串是否a=b
如我预期的那样非空。[ a=b ]; echo $?
0
出于同样的原因回声。[[ a==b ]]; echo $?
0
出于同样的原因回声。[ a==b ]; echo $?
0
出于同样的原因回声。[[ a!=a ]]; echo $?
两者[ a!=a ]; echo $?
都回显0
(预期)
因此,条件表达式中似乎只能省略<
and周围的空格,但如果目的是进行字符串比较,则不能省略 or or 。但为什么要这样设计呢?这也可能是我的遗漏,但这似乎没有在 bash 手册中的任何地方记录。>
=
==
!=
我最初的问题是尝试使用>
未转义作为条件表达式 ( [[ ... ]]
) 中模式的一部分。我最初认为,只要我不用>
空格包围,我应该能够毫无问题地使用它,因为在条件内进行重定向是没有意义的(并且在一些测试后证明是不可能的)表达也可以。
然而事实证明并非如此。当然,简单的解决方案就是逃避它>
并编写\>
,但我不明白为什么需要它。
这是我用于测试的 bash 版本,
GNU bash, version 5.2.2(1)-release (aarch64-unknown-linux-android)
和
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
我的实验是在env -i bash --norc --noprofile
.
答案1
这归结为这样一个事实:像&
, ;
, (
, |
, <
, >
, tab 这样的字符是元字符在 shell 语法中。它们形成自己的标记(或者可以组合成更多标记,例如;;
, |&
, ||
...)。这与[
、{
、!
、-
和等字符不同=
,这些字符类似于其中的字母 ² 和数字。
在 Korn shell 的构造内部[[...]]
,shell 理解一种单独的微语言,但它遵循与外部大致相同的标记规则。
出于同样的原因,在 之外[[...]]
,您可以执行以下操作:
(echo a>file|tr a b&)
并且不必写它:
( echo a > file | tr a b & )
或者甚至是类似的东西if((1))then<file&&(uname)fi
。
在这里,您可以执行以下操作:
[[(a>b||b<d)]]
as (
、>
、|
和)
是 shell 元字符。
元字符必须用引号引起来(无论是用'...'
, "..."
, \
, $'...'
...)才能按字面意思理解。
[[x]]
无法工作,因为 shell 只能看到一个[[x]]
令牌。并且[[
启动该[[...]]
构造的关键字甚至不会被识别。[[ a==b ]]
与 不同[[ a == b ]]
,如在[[...]]
微语言中一样,a==b
是单个标记,因此其解释与 相同[[ -n 'a==b' ]]
。
[
本身只是一个普通命令,因此它的解析方式与其他命令相同。
[ a>b ]
[
使用a
and作为参数运行]
,其输出重定向到b
, 与 相同[ a ] > b
,就像与orecho a>b ]
相同。echo a ] > b
>b echo a ]
请注意,它的内部((...))
(也与 ksh)不同,它也带有自己的类似 C 的微语言,但这次标记化规则不同。例如,您可以编写((var=123+1))
or ((a==b))
,但不需要((var = 123 + 1))
or ((a == b))
。
1{
参与大括号扩展,也是 shell 保留字,但在标记化之后进行处理,[
但在解析赋值时参与标记化。a[1 + 1]=foo
在 bash 或 ksh(不是 zsh)中被解析为一个赋值词,而不是作为参数a[1
运行。是一个保留字,如,但也参与历史扩展,尽管这只适用于 shell 的交互式调用。+
1]=foo
!
{
-
² 请注意,一些运算符中涉及字母 和 ,[[...]]
例如-nt
, -eq
, -lt
...