*sh shell 中的反引号(即`cmd`)是否已被弃用?

slm*_*slm 141 shell scripting bash zsh shell-script

我在 Unix 和 Linux 以及其他使用短语“反引号已被弃用”的站点上多次看到此评论,这与 Bash 和 Zsh 等 shell 相关。

这个说法是真的还是假的?

slm*_*slm 144

“弃用”有两种不同的含义。

被弃用:(主要是软件功能)可用但被视为过时和最好避免,通常是由于已被取代。

——新牛津美式词典

根据这个定义不推荐使用反引号。

弃用状态也可能表示该功能将在未来被删除。

维基百科

根据这个定义,反引号被弃用。

仍然支持:

引用有关 Shell 命令语言的开放组规范,特别是第2.6.3命令替换,可以看出,就规范而言,仍然支持两种形式的命令替换,反引号 ( `..cmd..`) 或美元括号 ( $(..cmd..))。

命令替换允许将命令的输出替换为命令名称本身。当命令被如下包围时,将发生命令替换:

$(command)
          
Run Code Online (Sandbox Code Playgroud)

或(反引号版本):

`command`
Run Code Online (Sandbox Code Playgroud)

shell 应通过在子 shell 环境中执行命令来扩展命令替换(请参阅 Shell 执行环境)并将命令替换(命令文本加上封闭$()或反引号)替换为命令的标准输出,删除一个或多个序列<newline> 替换末尾的字符。输出结束前嵌入的 <newline> 字符不得删除;但是,它们可能会被视为字段分隔符并在字段拆分期间被消除,具体取决于 IFS 的值和有效的引用。如果输出包含任何空字节,则行为未指定。

在命令替换的反引号样式中,<backslash>应保留其字面含义,除非后面跟着: '$'、' `' 或<backslash>。匹配反引号的搜索应由第一个未加引号的非转义反引号满足;在此搜索过程中,如果在 shell 注释、here-document、$(command)表单的嵌入命令替换或带引号的字符串中遇到非转义反引号,则会出现未定义的结果。在 " `...`" 序列内开始但不结束的单引号或双引号字符串会产生未定义的结果。

对于$(command)表单,左括号后面到匹配右括号的所有字符构成命令。任何有效的 shell 脚本都可用于命令,除了仅由产生未指定结果的重定向组成的脚本。

那么为什么每个人都说反引号已被弃用呢?

因为大多数用例应该使用美元括号形式而不是反引号。(在上述第一种意义上已弃用。)许多最负盛名的网站(包括 U&L)也经常自始至终都声明这一点,因此这是合理的建议。这个建议不应该与一些不存在的计划相混淆,即从 shell 中删除对反引号的支持。

注意:第三个摘录(以上)继续展示了几种情况,其中反引号根本不起作用,但较新的美元括号方法可以,从以下段落开始:

此外,反引号语法对嵌入命令的内容有历史限制。虽然较新的“$()”表单可以处理任何类型的有效嵌入脚本,但反引号形式无法处理一些包含反引号的有效脚本。

如果您继续阅读该部分,则会突出显示失败,显示它们如何使用反引号失败,但请使用较新的美元括号表示法。

结论

因此,您最好使用美元括号而不是反引号,但实际上您并没有使用技术上“弃用”的东西,如“这将在某个计划点完全停止工作”。

阅读完所有这些后,您应该知道强烈建议您使用美元括号,除非您特别需要与真正的原始非 POSIX Bourne shell 兼容。

  • 根据 [_deprecated_ 的大部分含义](http://en.wikipedia.org/wiki/Deprecation),我(和你)会说反引号 _are_ 已弃用。这主要是一个术语问题。 (29认同)
  • @StephaneChazelas - 同意!我将继续告诉 ppl,它们已被弃用,但反引号尚未正式“弃用”,因为它们将在不久的将来从任何 shell 的代码库中积极删除。至少我不知道。 (4认同)
  • 我的解释是,支持反引号纯粹是因为它们在现有代码中根深蒂固,无法正式弃用。我从未听说过在新代码中继续使用它们的争论。 (4认同)
  • 您可能需要从 [POSIX 标准原理](http://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html#tag_23_02_06_03) 中添加以下引用: *由于这些不一致的行为,反引用的各种对于嵌套命令替换或尝试嵌入复杂脚本的新应用程序,不建议使用命令替换。* 虽然这不是正式的弃用,因为基本原理部分提供信息而不是规范,但它确实建议应避免继续使用反引号。 (4认同)
  • @slm 是的,我想你确实明白了 - 我被拉走了,没有注意到。几乎没有理由使用这些东西,除了在分层子外壳时它可以更干净一点,所以 `$( cmd \`cmd\`)` 比 `$( cmd $(cmd))` 少一点混乱- 更容易阅读。我一直认为软件中已弃用的功能是正式标记为由上游删除的功能 - 我从未听说过这样的事情。我不在乎 - 不爱坟墓。 (3认同)
  • @chepner - 我也没有,但是如果您遇到它们,那么理解其含义的人可能会选择它们而不是美元括号,这可能是一个很好的理由。但是很可能它们可能被用于 b/c 用户只是不明白在美元括号中还有其他选择。 (2认同)
  • 但是@slm 请注意,(与`goto`s 不同)使用反引号不会使您[被猛禽吃掉](http://xkcd.com/292/)。除非你嵌套它们。 (2认同)
  • ShellCheck 还表示弃用:https://github.com/koalaman/shellcheck/wiki/SC2006。只是评论:可惜了,视觉上我更喜欢反引号。 (2认同)

ken*_*orb 16

它没有被弃用,但反引号 ( `...`) 是最古老的非 POSIX 兼容的 bourne-shell 所需的遗留语法,并且$(...)是 POSIX 并且由于以下几个原因更受欢迎:

  • \反引号内的反斜杠 ( ) 以不明显的方式处理:

    $ echo "`echo \\a`" "$(echo \\a)"
    a \a
    $ echo "`echo \\\\a`" "$(echo \\\\a)"
    \a \\a
    # Note that this is true for *single quotes* too!
    $ foo=`echo '\\'`; bar=$(echo '\\'); echo "foo is $foo, bar is $bar" 
    foo is \, bar is \\
    
    Run Code Online (Sandbox Code Playgroud)
  • 在里面嵌套引用$()要方便得多:

    echo "x is $(sed ... <<<"$y")"
    
    Run Code Online (Sandbox Code Playgroud)

    代替:

    echo "x is `sed ... <<<\"$y\"`"
    
    Run Code Online (Sandbox Code Playgroud)

    或者写一些类似的东西:

    IPs_inna_string=`awk "/\`cat /etc/myname\`/"'{print $1}' /etc/hosts`
    
    Run Code Online (Sandbox Code Playgroud)

    因为$()使用全新的上下文来引用

    这是不可移植的,因为 Bourne 和 Korn shell 需要这些反斜杠,而 Bash 和 dash 不需要。

  • 嵌套命令替换的语法更简单:

    x=$(grep "$(dirname "$path")" file)
    
    Run Code Online (Sandbox Code Playgroud)

    比:

    x=`grep "\`dirname \"$path\"\`" file`
    
    Run Code Online (Sandbox Code Playgroud)

    因为$()强制使用全新的引用上下文,所以每个命令替换都受到保护并且可以单独处理,而无需特别关注引用和转义。当使用反引号时,它在两个及以上级别后变得越来越难看。

    再举几个例子:

    echo `echo `ls``      # INCORRECT
    echo `echo \`ls\``    # CORRECT
    echo $(echo $(ls))    # CORRECT
    
    Run Code Online (Sandbox Code Playgroud)
  • 它解决了使用反引号时行为不一致的问题:

    • echo '\$x' 产出 \$x
    • echo `echo '\$x'` 产出 $x
    • echo $(echo '\$x') 产出 \$x
  • 反引号语法对嵌入命令的内容有历史限制,不能处理一些包含反引号的有效脚本,而较新的$()形式可以处理任何类型的有效嵌入脚本。

    例如,这些在其他方面有效的嵌入脚本在左侧列中不起作用,但在右侧IEEE 中起作用:

    echo `                         echo $(
    cat <<\eof                     cat <<\eof
    a here-doc with `              a here-doc with )
    eof                            eof
    `                              )
    
    
    echo `                         echo $(
    echo abc # a comment with `    echo abc # a comment with )
    `                              )
    
    
    echo `                         echo $(
    echo '`'                       echo ')'
    `                              )
    
    Run Code Online (Sandbox Code Playgroud)

因此$-prefixed命令替换的语法应该是首选方法,因为它视觉清晰,语法清晰(提高人机可读性),可嵌套直观,内部解析分离,也更一致(与从双引号内解析的所有其他扩展),其中反引号是唯一的例外,并且`字符在相邻时很容易伪装,"使其更难以阅读,尤其是使用小字体或不寻常的字体时。

来源:为什么$(...)优先于`...`(反引号)?在 BashFAQ

也可以看看: