在shell脚本中使用$()而不是反引号的好处是什么?

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

  1. 传统的Bourne shell反引号``

      var=`command` 
  2. $()语法(据我所知是Bash具体)

      var=$(command) 

与反引号相比,使用第二个语法有什么好处? 还是两个完全等同于100%?

最主要的是能够嵌套他们,命令内的命令,而不会失去理智,试图找出是否某种forms的逃逸将反作用。

一个例子,虽然有点人为的做法:

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

这将给你一个在/dir目录树中所有与2011年12月(a)最早的date文本文件具有相同名称的文件列表。

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

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

(a)现在具体的命令可能实际上不起作用,我还没有testing过这个function。 所以,如果你为此投下我的意见,你已经看不到这个意图了:-)这只是一个说明你如何嵌套,而不是一个无bug的生产就绪片段。

假设你想find与gcc安装位置相对应的lib目录。 你有一个select:

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

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

从男人bash:

  $(command) or `command` Bash performs the expansion by executing command and replacing the com- mand substitution with the standard output of the command, with any trailing newlines deleted. Embedded newlines are not deleted, but they may be removed during word splitting. The command substitution $(cat file) can be replaced by the equivalent but faster $(< file). When the old-style backquote form of substitution is used, backslash retains its literal meaning except when followed by $, `, or \. The first backquote not preceded by a backslash terminates the command sub- stitution. When using the $(command) form, all characters between the parentheses make up the command; none are treated specially. 

反引号( `...` )是只有最老的非POSIX兼容的bourne-shells才需要的传统语法,而$(...)是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则不需要。

  • 嵌套命令replace的语法比较简单:

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

    比:

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

    因为$()强制引用一个全新的上下文,所以每个命令replace都是受保护的,并且可以自行处理,而不用引用和转义。 当使用反引号时,在两个或更多级别之后会变得更加丑陋和丑陋。

    几个例子:

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

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

    例如,这些其他有效的embedded式脚本不能在左列中工作,但是可以在正确的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 命令replace的语法应该是首选的方法,因为它清晰的语法清晰(提高了人机可读性),它是可嵌套和直观的,它的内部parsing是分离的,并且也是更一致的与所有其他扩展在双引号内parsing),反引号是唯一的例外, `字符很容易被伪装在邻近" ,这使得阅读变得更加困难,特别是对于小字体或不寻常的字体。

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

也可以看看:

  • POSIX标准部分“2.6.3命令replace”
  • 包含$()语法的POSIX基本原理
  • 命令replace
  • bash-hacker:命令replace

除了其他答案之外,

 $(...) 

在视觉上比从视觉上更好

 `...` 

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

(而且,正如我刚才注意到的,反引用代码很难进入内联代码示例。)

$()允许嵌套。

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

我觉得反驳是不允许的。

这是POSIX标准,它定义了$(command)forms的命令replace。 目前使用的大多数shell都是POSIX兼容的,并且支持这种优先的forms而不是古老的反引号。 Shell语言文档的命令replace部分(2.6.3)描述了这一点:

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

$( command )

或(反引号):

` command `

shell将通过在子shell环境中执行命令来扩展命令replace(参见Shell执行环境 ),并用命令的标准输出replace命令replace(command的文本加上括号“$()”或反引号),移除replace结尾处的一个或多个<newline>字符的序列。 在输出结束之前embedded的<newline>字符不得被删除; 然而,它们可能会被视为字段分隔符,并在字段拆分期间被删除,具体取决于IFS的价值和引用是否有效。 如果输出包含任何空字节,则行为是未指定的。

在反引用的命令replace样式中, <backslash>应保留其字面含义,除非后面跟着:' $ ',' ` '或<backslash> 。 search匹配的反引号应该由第一个不带引号的非反引号来满足; 在search期间,如果在shell注释,here-document,$( command )表单或带引号的string的embedded式命令replace中遇到未转义的反引号,则会发生未定义的结果。 在“ `...` ”序列中开始但不结束的单引号或双引号string会产生未定义的结果。

使用$( command )forms,左括号后面的所有字符构成命令 。 任何有效的shell脚本都可以用于命令 ,除了仅由redirect组成的脚本以外,它们会产生未指定的结果。

命令replace的结果不得进行进一步的代字符扩展,参数扩展,命令replace或算术扩展。 如果在双引号内发生命令replace,则不应对replace结果执行字段拆分和path名扩展。

命令replace可以嵌套。 要指定反向引用版本中的嵌套,应用程序应在内部反引号之前加上<backslash>字符; 例如:

\` command \`

shell命令语言的语法对于以“ $(( ”)开头的扩展是不明确的,它可以引入一个算术扩展或者以subshel​​l开头的命令replace。算术扩展有优先权,也就是说,它可以将扩展parsing为一个算术扩展,只有当它确定不能将扩展parsing为一个算术扩展时,才会将扩展parsing为一个命令replace,在执行这个确定时,shell不需要评估嵌套扩展。在没有确定不能将扩展parsing为算术扩展的情况下,shell将扩展视为一个不完整的算术扩展并报告一个语法错误,一个合适的应用程序应确保它将“ $( ”和“ ( '换成两个令牌(换句话说,用空格隔开),以一个子shell开始的命令replace。例如,一个通讯 并且包含一个子shell的replace可以写成:

$( ( command ) )

这是一个遗留问题,但是我想出了一个完美有效的例子$(...)

我正在使用远程桌面来运行cygwin的窗口,并想迭代一个命令的结果。 可悲的是,无论是远程桌面还是cygwin本身,反引号字符都无法进入。

假设美元符号和圆括号可以更容易地input这种奇怪的设置是理智的。