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

DVK*_*DVK 149 syntax sh backticks

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

  1. Legacy Bourne shell反手``:

     var=`command`
    
    Run Code Online (Sandbox Code Playgroud)
  2. $() 语法(据我所知,特定于Bash,或至少不受原始Bourne等非POSIX旧shell支持)

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

与反引号相比,使用第二种语法有什么好处吗?或两者完全相同?

pax*_*blo 139

主要的是能够嵌套它们,命令中的命令,而不会失去理智,试图找出某种形式的转义是否会对反引号起作用.

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

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

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

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

pax> cd /home/pax/xyzzy/plugh
pax> parent=$(basename $(dirname $PWD))
pax> echo $parent
xyzzy
Run Code Online (Sandbox Code Playgroud)

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

  • 我希望SO上的所有代码都是生产就绪片段,专为NASA穿梭码可靠性标准而设计.少得到一个标志和删除投票. (71认同)
  • @DVK http://en.wikipedia.org/wiki/Mars_Climate_Orbiter#Cause_of_failure (12认同)
  • @chrstphrchvz,如果你查看我的个人资料,你会看到这个小片段:"我在Stack Overflow上发布的所有代码都包含在"随便用它做什么"许可证中,其全文如下:做任何你想要的东西.":-)无论如何,我很确定这是DVK的幽默. (9认同)
  • @DVK 如果你不是在开玩笑,我不同意代码贡献应该被标记为未能自动从 SO 默认(CC-BY-SA 或 MIT 许可证)重新许可,以允许此类保证或目的的适用性。相反,我会自担风险在 SO 上重用代码,并根据帮助、技术优点等对贡献进行投票。 (4认同)

Jon*_*ler 56

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

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

libdir=`dirname \`dirname \\\`which gcc\\\`\``/lib
Run Code Online (Sandbox Code Playgroud)

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

  • 在这些命令替换周围看到一些引用会很好! (3认同)

ken*_*orb 31

backticks(`...`)是最老的非POSIX兼容的bourne-shell所需的遗留语法,并且$(...)由于以下几个原因而更为首选:

  • \反引号内的反斜杠()以非显而易见的方式处理:

    $ 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
  • Backticks语法对嵌入式命令的内容有历史限制,无法处理包含反引号的一些有效脚本,而较新的$()表单可以处理任何类型的有效嵌入式脚本.

    例如,这些其他有效的嵌入式脚本在左列中不起作用,但可以在正确的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

也可以看看:


dgw*_*dgw 22

来自man 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.
Run Code Online (Sandbox Code Playgroud)


Kei*_*son 9

除了其他答案,

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

在视觉上更胜一筹

`...`
Run Code Online (Sandbox Code Playgroud)

反叛看起来太像撇号; 这取决于您使用的字体.

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

  • 你必须有一个奇怪的键盘(或者我呢?).对我来说,输入反引号要容易得多 - 它们是左上角键,不需要SHIFT或ALT. (2认同)
  • @DVK:我说的是它们的外观,而不是打字的方便。(我的键盘可能和你的一样。)不过,既然你提到了它,我认为我对`$``(` 和`)` 的肌肉记忆比我对反引号有更好的记忆;天啊。 (2认同)

Shi*_*dim 6

$() 允许嵌套.

out=$(echo today is $(date))
Run Code Online (Sandbox Code Playgroud)

我认为反引号不允许.

  • 您可以嵌套反引号;这要困难得多:```out =`echo今天是\`date \`````。 (2认同)

JRF*_*son 5

POSIX 标准定义了$(command)命令替换的形式。今天使用的大多数 shell 都符合 POSIX 标准,并支持这种首选形式而不是古老的反引号符号。Shell Language 文档的命令替换部分 (2.6.3) 描述了这一点:

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

$(command)

或(反引号版本):

`command`

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

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

在 $( command ) 形式中,从左括号到匹配右括号的所有字符构成 command。任何有效的 shell 脚本都可以用于command,除了仅由重定向组成的脚本会产生未指定的结果。

命令替换的结果不应被处理以进行进一步的波浪号扩展、参数扩展、命令替换或算术扩展。如果命令替换发生在双引号内,则不应对替换结果执行字段拆分和路径名扩展。

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

\`command\`

shell 命令语言的语法对于以“$((", 可以引入算术展开或以子shell开头的命令替换。算术展开有优先权;也就是说,shell首先判断是否可以将展开解析为算术展开,而只将展开解析为命令如果它确定它不能将扩展解析为算术扩展,则替换。执行此确定时,shell 不需要评估嵌套扩展。如果它遇到输入的结尾,而尚未确定它无法将扩展解析为算术扩展,则shell 应将扩展视为不完整的算术扩展并报告语法错误。符合要求的应用程序应确保将 " $(" 和 '分开(' 在以子shell 开头的命令替换中分成两个标记(即,用空格分隔它们)。例如,包含单个子 shell 的命令替换可以写为:

$( (command) )


Pio*_*fer 5

这是一个遗留问题,但我想出了一个完全有效的$(...)over示例`...`

我正在使用远程桌面到运行 cygwin 的 Windows 并希望迭代命令的结果。遗憾的是,由于远程桌面或 cygwin 本身,无法输入反引号字符。

假设美元符号和括号在这种奇怪的设置中更容易输入是明智的。