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

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

我在 Unix 和 Linux 以及其他使用“反引号已被弃用”措辞的网站上多次看到此评论,涉及 Bash 和 Zsh 等 shell。

这个说法是真是假?

答案1

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

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

——新牛津美国词典

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

已弃用状态也可能表明该功能将来将被删除。

维基百科

根据这个定义,反引号是不是已弃用。

仍然支持:

引用 Open Group Shell 命令语言规范,特别是部分2.6.3 命令替换,可以看出,在规范范围内,两种形式的命令替换、反引号 ( `..cmd..`) 或美元括号 ( ) 仍然受支持。$(..cmd..)

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

$(command)
          

或(反引号版本):

`command`

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

在命令替换的反引号样式中,<反斜杠>应保留其字面含义,除非后面跟着:“$”、“ `”或<反斜杠>。对匹配反引号的搜索应由第一个未加引号的非转义反引号来满足;在此搜索期间,如果在 shell 注释、此处文档、嵌入式命令替换形式$(command)或带引号的字符串中遇到非转义反引号,则会出现未定义的结果。在 " " 序列中开始但不结束的单引号或双引号字符串`...`会产生未定义的结果。

对于该$(command)形式,左括号后面到匹配右括号的所有字符构成命令。任何有效的 shell 脚本都可用于命令,但仅包含产生未指定结果的重定向的脚本除外。

那么为什么大家都说反引号已被弃用呢?

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

  • BashFAQ #082 - 为什么 $(...) 优于 `...`(反引号)?

    `...`是只有最古老的非 POSIX 兼容 bourne-shell 才需要的遗留语法。总是偏爱语法有几个原因$(...)

    ...

  • Bash Hackers Wiki - 过时且已弃用的语法

    这是较旧的 Bourne 兼容形式命令替换`COMMANDS`和语法均由$(COMMANDS)POSIX 指定,但后者是大大地首选,尽管不幸的是前者在脚本中仍然非常普遍。每个现代 shell(以及其他一些 shell)都广泛实现了新式命令替换。使用反引号的唯一原因是为了与真正的 Bourne shell(如 Heirloom)兼容。反引号命令替换在嵌套时需要特殊转义,并且在野外发现的示例常常被不正确地引用。看: 为什么 $(...) 优于 `...`(反引号)?

  • POSIX 标准原理

    由于这些不一致的行为,对于嵌套命令替换或尝试嵌入复杂脚本的新应用程序,不建议使用反引号命令替换。

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

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

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

结论

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

阅读完所有这些内容后,您应该明白,强烈建议您使用美元括号,除非您具体来说需要与真正的原始非 POSIX Bourne shell 兼容。

答案2

它并未被弃用,但反引号 ( `...`) 是仅最古老的非 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 \\
    
  • 内部嵌套引用$()要方便得多:

    echo "x is $(sed ... <<<"$y")"
    

    代替:

    echo "x is `sed ... <<<\"$y\"`"
    

    或者写一些类似的东西:

    IPs_inna_string=`awk "/\`cat /etc/myname\`/"'{print $1}' /etc/hosts`
    

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

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

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

    x=$(grep "$(dirname "$path")" file)
    

    比:

    x=`grep "\`dirname \"$path\"\`" file`
    

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

    再举几个例子:

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

    • 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 ')'
    `                              )
    

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

来源:为什么$(...)优先于`...`(反引号)?在 Bash 常见问题解答

也可以看看:

答案3

请注意,您的标题有点误导性并且与您的问题不同,所以让我们回答标题中的问题(并且绝不不同意有关 sh/bash 变体的正确答案)。

您的问题文本具体指的是 bash/zsh,但您的标题询问的是 *贝壳。谈论*贝壳是一个更广泛问题,并且规范地包括像 csh 和 tcsh 这样仍然被大量使用的 shell。

是的,我知道很多人讨厌 tcsh,他们经常给出各种各样的理由为什么你不应该使用 tcsh 作为脚本语言(我同意)并忽略简单地将其用作交互式 shell 的可能性。 (坦白说,我不确定为什么你使用任何 *sh 作为编程语言......但让我们继续)。

在 csh/tcsh 中,反引号肯定是不是已弃用,部分原因是它们确实不是支持$()。

所以我们知道 bash 变体“不推荐使用”反引号,因为不推荐使用反引号,但是不是从某种意义上说,它将很快被删除(或者可能永远被删除)。我们还知道 csh/tcsh 支持反引号。

那么,人们可以提出一个像样的论点,即在回答 stackexchange 等网站上的问题时,实际上应该使用反引号来表示简单的示例,这些示例不涉及任何复杂的引用,而更喜欢 $() 解决方案。这样,答案就更加可移植,而不是仅仅直接为 bash 用户解决答案,并且使可能使用不同 shell 并且可能不知道 $() bourne 语法的不太高级的用户感到困惑。

作为旁注和另一个常见问题,可以更方便地给出将环境变量导出到命令的示例,而不是:

% VAR=value some_command arg1 arg2 ...

这只适用于 sh/bash 变体,可以简单地执行以下操作:

% env VAR=value some_command arg1 arg2 ...

神奇的是,你的答案现在适用于大多数 shell。

像 stackexchange 这样的网站上的全局答案通常应该是为尽可能多的用户解决问题而尽可能少地混淆,而不是对应该使用哪种 shell 采取立场。

我意识到这与您原来的(诚然是较旧的)问题有点相切,但问题是这个问题的答案被引用以建议人们应该在 stackexchange 答案中使用 $() ,所以脚注可能是值得一提。

相关内容