226

有两种方法可以捕获命令行的输出bash

  1. 旧版 Bourne shell 反引号``

    var=`command`
    
  2. $()语法(据我所知,它是特定于 Bash 的,或者至少不受非 POSIX 旧外壳(如原始 Bourne)支持)

    var=$(command)
    

与反引号相比,使用第二种语法有什么好处吗?还是两者完全 100% 等效?

4

9 回答 9

217

最主要的是能够将它们嵌套在命令中,命令中的命令,而不会失去理智,试图弄清楚某种形式的转义是否会对反引号起作用。

一个例子,虽然有点做作:

deps=$(find /dir -name $(ls -1tr 201112[0-9][0-9]*.txt | tail -1l) -print)

这将为您提供目录树中与 2011 年 12 月(a)/dir中最早日期的文本文件同名的所有文件的列表。

另一个例子是获取父目录的名称(不是完整路径):

pax> cd /home/pax/xyzzy/plugh
pax> parent=$(basename $(dirname $PWD))
pax> echo $parent
xyzzy

(a)既然特定命令可能实际上不起作用,我还没有测试过它的功能。所以,如果你投票给我,你就忽略了它的意图:-) 它只是作为你如何嵌套的说明,而不是作为一个无错误的生产就绪片段。

于 2012-02-26T01:40:13.797 回答
75

假设要查找gcc安装位置对应的lib目录。你有一个选择:

libdir=$(dirname $(dirname $(which gcc)))/lib

libdir=`dirname \`dirname \\\`which gcc\\\`\``/lib

第一个比第二个更容易 - 使用第一个。

于 2012-02-26T02:03:04.913 回答
50

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

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

也可以看看:

于 2015-10-23T11:36:50.517 回答
27

来自 man bash:

       $(命令)
或者
       `命令`

Bash 通过执行命令并替换 com- 来执行扩展
用命令的标准输出命令替换,任何
尾随换行被删除。嵌入的换行符不会被删除,但它们
可以在分词期间删除。命令替换 $(cat
file) 可以替换为等效但更快的 $(< file)。

当使用旧式反引号形式的替换时,反斜杠
保留其字面意义,除非后面跟有 $、` 或 \。这
前面没有反斜杠的第一个反引号会终止命令 sub-
制度。当使用 $(command) 形式时,所有字符之间的
括号构成命令;没有人受到特殊对待。
于 2012-02-26T01:41:59.467 回答
11

除了其他答案,

$(...)

在视觉上比

`...`

反引号看起来太像撇号了;这取决于您使用的字体。

(而且,正如我刚刚注意到的,反引号更难输入内联代码示例。)

于 2012-02-26T01:58:27.023 回答
9

$()允许嵌套。

out=$(echo today is $(date))

我认为反引号不允许这样做。

于 2012-02-26T01:44:03.890 回答
5

It is the POSIX standard that defines the $(command) form of command substitution. Most shells in use today are POSIX compliant and support this preferred form over the archaic backtick notation. The command substitution section (2.6.3) of the Shell Language document describes this:

Command substitution allows the output of a command to be substituted in place of the command name itself.  Command substitution shall occur when the command is enclosed as follows:

$(command)

or (backquoted version):

`command`

The shell shall expand the command substitution by executing command in a subshell environment (see Shell Execution Environment) and replacing the command substitution (the text of command plus the enclosing "$()" or backquotes) with the standard output of the command, removing sequences of one or more <newline> characters at the end of the substitution. Embedded <newline> characters before the end of the output shall not be removed; however, they may be treated as field delimiters and eliminated during field splitting, depending on the value of IFS and quoting that is in effect. If the output contains any null bytes, the behavior is unspecified.

Within the backquoted style of command substitution, <backslash> shall retain its literal meaning, except when followed by: '$' , '`', or <backslash>. The search for the matching backquote shall be satisfied by the first unquoted non-escaped backquote; during this search, if a non-escaped backquote is encountered within a shell comment, a here-document, an embedded command substitution of the $(command) form, or a quoted string, undefined results occur. A single-quoted or double-quoted string that begins, but does not end, within the "`...`" sequence produces undefined results.

With the $(command) form, all characters following the open parenthesis to the matching closing parenthesis constitute the command. Any valid shell script can be used for command, except a script consisting solely of redirections which produces unspecified results.

The results of command substitution shall not be processed for further tilde expansion, parameter expansion, command substitution, or arithmetic expansion. If a command substitution occurs inside double-quotes, field splitting and pathname expansion shall not be performed on the results of the substitution.

Command substitution can be nested. To specify nesting within the backquoted version, the application shall precede the inner backquotes with <backslash> characters; for example:

\`command\`

The syntax of the shell command language has an ambiguity for expansions beginning with "$((", which can introduce an arithmetic expansion or a command substitution that starts with a subshell. Arithmetic expansion has precedence; that is, the shell shall first determine whether it can parse the expansion as an arithmetic expansion and shall only parse the expansion as a command substitution if it determines that it cannot parse the expansion as an arithmetic expansion. The shell need not evaluate nested expansions when performing this determination. If it encounters the end of input without already having determined that it cannot parse the expansion as an arithmetic expansion, the shell shall treat the expansion as an incomplete arithmetic expansion and report a syntax error. A conforming application shall ensure that it separates the "$(" and '(' into two tokens (that is, separate them with white space) in a command substitution that starts with a subshell. For example, a command substitution containing a single subshell could be written as:

$( (command) )

于 2012-02-26T13:53:50.713 回答
5

我想出了一个完全有效的例子$(...)over `...`

我在运行Cygwin的 Windows 上使用远程桌面,并想遍历命令的结果。可悲的是,由于远程桌面或 Cygwin 本身,无法输入反引号字符。

假设美元符号和括号会更容易在这种奇怪的设置中键入是正常的。

于 2015-12-04T11:10:48.487 回答
1

在 2021 年,值得一提的是一个奇怪的事实,作为对其他答案的补充。

管道的 Microsoft DevOps YAML “脚本”可能包括Bash 任务。但是,该符号$()用于引用在 YAML 上下文中定义的变量,因此在这种情况下,应该使用反引号来捕获命令的输出。

这主要是在将脚本代码复制到 YAML 脚本时出现的问题,因为 DevOps 预处理器对不存在的变量非常宽容,因此不会出现任何错误消息。

于 2021-03-11T13:36:18.163 回答