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