如何理解关于 `(`, `)` 和 `test` 的 POSIX 描述?

如何理解关于 `(`, `)` 和 `test` 的 POSIX 描述?

POSIX 2013:

指定 -a 和 -o 二进制原色以及“(”和“)”运算符的 XSI 扩展已被标记为过时。 (许多使用它们的表达式是由语法模糊定义的,具体取决于正在评估的特定表达式。)使用这些表达式的脚本应转换为下面给出的形式。尽管许多实现将继续支持这些过时的形式,但脚本在处理用户提供的输入时应该非常小心,因为这些输入可能会与这些和其他原色和运算符混淆。除非应用程序开发人员知道向脚本生成输入的所有情况,否则调用如下:

test "$1" -a "$2"

应写为:

test "$1" && test "$2"

  1. “‘(’和‘)’运算符已被标记为过时”。

    (和 是)对命令进行分组并创建子 shell 的运算符吗?

    如果它们已经过时,那么它们的替代品是什么?

  2. 应该test "$1" -a "$2"替换为test "$1" && test "$2",还是替换为(( test "$1" && test "$2" ))

    我们不需要像原始命令((...))一样返回 0 或 1吗?test

答案1

(和 是)对命令进行分组并创建子 shell 的运算符吗?

不,该文档引用了使用以下方式对表达式进行分组的运算符test

test 0 -eq 0 -a \( 0 -eq 1 -o 1 -eq 1 \)

如果它们已经过时,那么它们的替代品是什么?

它们的替代品是(){},类似地,它们在 shell 级别对命令进行分组:

test 0 -eq 0 && (test -0 -eq 1 || test 1 -eq 1)
test 0 -eq 0 && { test -0 -eq 1 || test 1 -eq 1; }

很容易看出这种模式:testfor 表达式中使用的每个运算符都被替换为 shell 中等效的 for 命令。

应该test "$1" -a "$2"替换为test "$1" && test "$2",还是替换为(( test "$1" && test "$2" ))

应将其替换为test "$1" && test "$2";(())用于算术扩展,与命令的退出状态无关,因为其目的是计算算术表达式。例如,这将是有效的:

(( $(test 1 -eq 1 && echo $?) && $(test 0 -eq 0 && echo $?) ))

(注意命令替换,它被替换为内部命令的退出状态,0并且0;计算的表达式实际上是(( 0 && 0 )))。

但这会导致语法错误:

(( test 1 -eq 1 && test 0 -eq 0 ))
$ (( test 1 -eq 1 && test 0 -eq 0 ))
bash: ((: test 1 -eq 1 && test 0 -eq 0 : syntax error in expression (error token is "1 -eq 1 && test 0 -eq 0 ")

答案2

用更简单的语言强调一个非常重要的事实:

您在屏幕上键入的字符可能会或可能不会被您调用的命令“看到”(接收)。您可能对此很熟悉;每次你引用某些东西时都会发生这种情况。

看到您输入的每个字符。 shell 对于如何处理这些字符有自己的规则。例如,该echo命令在以下每个实例中“看到”完全相同的内容:

$ echo hello
hello
$ echo "hello"
hello
$ echo 'hello'
hello
$ echo \h\e\l\l\o
hello
$ echo 'h'"e"\l$'lo'
hello
$ somevar=hello;echo $somevar
hello
$ somevar='h'"el"\l\o;echo $somevar
hello
$ 

就像命令永远不会“看到”上面的引号和反斜杠一样echo,shell 会专门处理括号。所以,当未引用时,除了外壳之外,其他任何东西都看不到它们。

一个很好的例子是命令find,它接受括号作为论点。对于括号抵达命令find并且不会由 shell 进行特殊解释,因此必须将它们加引号或转义以删除其对 shell 的特殊含义。请注意,引号和转义符确实不是到达find命令;只有括号可以:

(从另一个问题在这个网站上:)

# What you type:
find ~ \( -type f '-ex'ec ch\mod 600 {} + ')' -o \
       "(" -type d -exec c"hmo"d 700 {} ';' \)
# What the find command sees:
/home/tim ( -type f -exec chmod 600 {} + ) -o ( -type d -exec chmod 700 {} ; )
# If you typed this...
find ~ \( -type f -exec chmod 600 {} + ) -o \
       \( -type d -exec chmod 700 {} + \)
# ...find doesn't see anything, because bash runs into an error condition
# before it can even parse the line.  That close paren is special to bash.

还有一个与此相关的俚语。当您打算让命令“看到”特殊字符(通常是单引号或双引号,但也可以包含反斜杠或空格字符)并且由于引用和转义中的错误,该命令永远看不到该字符时,我们说 shell“吃掉”了引用。

一个有趣的吃引用的例子(以及围绕它的困难)包括:编写一个 ssh 命令,该命令将登录到远程服务器,启动 bash 会话,并运行命令sed来修改存储在文件,以便正确引用它以包含在 HTTP 消息中。


话虽如此,这是一个简短的答案:

  1. “‘(’和‘)’运算符已被标记为过时”。

( 和 ) 是对命令进行分组并创建子 shell 的运算符吗?

如果它们已经过时,那么它们的替代品是什么?

括号对于 shell 仍然具有特殊含义,并且并未过时。过时的是将括号传递给test命令(通过引用它们以防止 shell 吃掉它们)。


*这显然取决于您对“乐趣”的定义。

相关内容