Shell 为什么\$在反报价中减少到$[虽然不在$(…)]中]?

Shell 为什么\$在反报价中减少到$[虽然不在$(…)]中]?,shell,unix,posix,Shell,Unix,Posix,回顾POSIX标准,我遇到了另一个相当技术性/无意义的问题。它: 在命令替换的后引样式中,应保留其字面含义,除非后跟:“$”、“`”或” 很容易理解为什么“`”和“\”会失去字面意义:嵌套命令替换要求在命令替换中使用“不同”的反引号,这反过来会迫使“\”失去字面意义。因此,例如,以下不同的行为似乎是合理的: $ echo $(echo \\\\) \\ $ echo `echo \\\\` \ 但是“$”呢?也就是说,以下差异的意义是什么,或者更具体地说,可能带来的好处是什么 $ echo $

回顾POSIX标准,我遇到了另一个相当技术性/无意义的问题。它:

在命令替换的后引样式中,
应保留其字面含义,除非后跟:“
$
”、“
`
”或

很容易理解为什么“
`
”和“
\
”会失去字面意义:嵌套命令替换要求在命令替换中使用“不同”的反引号,这反过来会迫使“\”失去字面意义。因此,例如,以下不同的行为似乎是合理的:

$ echo $(echo \\\\)
\\
$ echo `echo \\\\`
\
但是“$”呢?也就是说,以下差异的意义是什么,或者更具体地说,可能带来的好处是什么

$ echo $(echo \$\$)
$$
$ echo `echo \$\$`
4735
由于在反引号中不排除“$”本身,因此看起来您会一直使用“$”或“\\\$”,但不会使用中间的“\$”

概括地说

$ echo `echo $$` # PID, OK
4735
$ echo `echo \\\$\\\$` # literal "$$", OK
$$
$ echo `echo \$\$` # What's the point?
4735

PS:我知道这个问题相当技术性。。。我自己一直在追求更现代的
$(…)
替换,但我仍然很好奇。

通过添加
\
,您可以使内部子shell扩展它,而不是外壳。一个很好的例子是强制启动一个新外壳,如下所示:

$ echo $$
4988
$ echo `sh -c 'echo $$'`
4988
$ echo `sh -c 'echo \$\$'`
4990
$ echo `sh -c 'echo \\\$\\\$'`
$$
基本答案 考虑以下命令,该命令查找安装了
gcc
的基本目录:

gcc_base=$(dirname $(dirname $(which gcc)))
使用
$(…)
符号,解析没有问题;这很简单,也是推荐使用符号的主要原因之一。使用反勾号的等效命令为:

gcc_base=`dirname \`dirname \\\`which gcc\\\`\``
当shell第一次解析这个命令时,它会遇到第一个backtick,并且必须找到匹配的close backtick。此时引用的部分生效:

在命令替换的后引号样式中,应保留其字面含义,除非后跟:“$”、“`”或

  • 反斜杠反勾-特殊规则
  • 反斜杠反斜杠-特殊规则
  • 反斜杠反勾-特殊规则
  • 反斜杠反斜杠-特殊规则
  • 反斜杠反勾-特殊规则
  • 反斜杠反勾-特殊规则
  • 因此,末尾的未转义backtick标记最外层backtick命令的结束。处理该命令的子shell可以看到:

    dirname `dirname \`which gcc\``
    
    再次对反斜杠逃生阀进行特殊处理,子壳体看到:

    dirname `which gcc`
    
    • 子shell可以查看
      哪个gcc
      ,并对其进行评估(例如
      /usr/gcc/v4.6.1/bin/gcc
    • 子shell评估
      dirname/usr/gcc/v4.6.1/bin/gcc
      ,并生成
      /usr/gcc/v4.6.1/bin
    • 子shell评估
      dirname/usr/gcc/v4.6.1/bin
      ,并生成
      /usr/gcc/v4.6.1
    • shell将
      /usr/gcc/v4.6.1
      分配给
      gcc\u base
    在本例中,反斜杠后面只跟有特殊字符-反斜杠、反勾号、美元。例如,一个更复杂的示例将在命令中包含
    \“
    序列,然后特殊规则将不适用;只需将
    \”
    复制到相应的子shell中,而不做任何更改

    非常复杂的东西 例如,假设您有一个名称中有一个空格的命令(但愿如此;这说明了原因!),例如
    完全令人惊奇
    (两个空格;这是一个比单个空格更严格的测试)。然后你可以写:

    $ cmd="totally  amazing"
    $ echo "$cmd"
    totally  amazing
    $ which "$cmd"
    /Users/jleffler/bin/totally  amazing
    $ dirname $(which "$cmd")
    usage: dirname path
    $ # Oops!
    $ dirname "$(which \"\$cmd\")"
    "$cmd": not found
    .
    $ # Oops!
    $ dirname "$(which \"$cmd\")"
    "totally: not found
    amazing": not found
    .
    $ dirname "$(eval which \"$cmd\")"
    totally amazing: not found
    .
    $ dirname "$(eval which \"\$cmd\")"
    /Users/jleffler/bin
    $ # Ouch, but at least that worked!
    $ # But how to extend that to the next level?
    $ dirname "$(eval dirname \"\$\(eval which \\\"\\\$cmd\\\"\)\")"
    /Users/jleffler
    $
    
    好吧,这是“简单”的一个!是否需要更好的理由来避免在命令名或路径名中使用空格?我还满意地证明了它可以正确地处理包含空格的路径名

    那么,我们可以压缩回跳的学习周期吗?是的

    $ cat x3.sh
    cmd="totally  amazing"
    which "$cmd"
    dirname "`which \"$cmd\"`"
    dirname "`dirname \"\`which \\"\$cmd\\\"\`\"`"
    $ sh -x x3.sh
    + cmd='totally  amazing'
    + which 'totally  amazing'
    /Users/jleffler/bin/totally  amazing
    ++ which 'totally  amazing'
    + dirname '/Users/jleffler/bin/totally  amazing'
    /Users/jleffler/bin
    +++ which 'totally  amazing'
    ++ dirname '/Users/jleffler/bin/totally  amazing'
    + dirname /Users/jleffler/bin
    /Users/jleffler
    $
    

    这仍然是一套可怕的、令人畏惧的、非直觉的逃跑序列。它实际上比
    $(…)
    表示法的版本短,并且不使用任何
    eval
    命令(这总是使事情复杂化)

    反斜杠基本上是转义字符。你把它放在另一个字符前面来表示一些特殊的东西。“n”、“t”、“$”和“\”是这些特殊字符

    "\n" --> newline
    "\t" --> tab (indent)
    "\$" --> $ (because a $ before a word in shell denotes a variable)
    "\\" --> \ 
    
    字符前的反斜杠只有在引号内时才按上述方式解释


    如果您想找到更多信息或其他转义符,请转到

    ,这可能与Bourne shell解析替换的奇怪方式有关(真正的Korn shell稍有相似,但大多数其他shell根本没有表现出奇怪的行为)

    基本上,Bourne shell的解析器不会在双引号内解释替换($和`),也不会在任何地方解释参数替换($)。这仅在扩展时完成。此外,在许多情况下,不匹配的引号(单引号、双引号或反引号)不是错误;最后假定为收盘报价

    一个结果是,如果在双引号外使用包含空格(如
    ${v+ab}
    )的单词替换参数,则无法正确解析该参数,并且在执行时会导致扩展错误。空间需要被引用。其他Shell没有此问题

    另一个结果是双引号内的双引号不能可靠地工作。比如说,

    v=0; echo "`v=1; echo " $v "`echo b"
    
    将打印

     1 echo b
    
    在大多数shell中(一个命令替换),但是

    在Bourne shell和real Korn shell(ksh93)中(两个命令替换)

    (避免上述问题的方法是首先将替换指定给变量,因此不需要双引号,或者使用新样式的命令替换。)

    真正的KornShell(ksh93)试图保留许多奇怪的BourneShell行为,但在解析时解析替换。因此,
    ${v+ab}
    是可以接受的,但是上面的例子有“奇怪”的行为。更奇怪的是

    echo "`${v+pwd"
    
    被接受(结果与小姐一样
     0 b
    
    echo "`${v+pwd"
    
    echo "`${v+pwd`"
    
    $ echo ${.sh.version}
    Version JM 93u 2011-02-08
    $ v=0; echo "`v=1; echo "${v+p q}"`echo b" 
    p qecho b
    $ v=0; echo "`v=1; echo "\${v+p q}"`echo b" 
    p{ q}b