当你做这样的事情时:
echo 'Hello World'
或者像这样:
x=12345
echo "x is: $x"
在第一个示例中,echo
命令是接收'Hello World'
,还是接收Hello World
?
在第二个示例中,echo
命令是接收"x is: $x"
,还是接收x is: 12345
?
所以基本上我的问题是:单引号和双引号是由bash
还是由处理echo
?
答案1
引号、变量扩展、通配符以及中提到的所有其他内容bash 手册中有关扩展的部分由 bash 处理。
例如,当您运行 bash 命令时echo "x is: $x"
,bash 会解析该命令以发现它需要使用echo
一个参数运行该命令,即x is: 12345
。给定echo "x is" "$x"
, bashecho
使用两个参数运行:x is
and 12345
— 这就是为什么保留引号内的两个空格(它们是 echo 打印未修改的参数的一部分),但引号外的两个空格则不保留(对于 shell,两个空格)分隔参数的空格与单个参数一样好,并且echo
始终在中间打印一个空格)。
该echo
命令(或任何其他命令)无法知道哪个 shell 命令产生了参数x is: 12345
,或者实际上是否涉及 shell。以下是产生此参数的一些示例命令:
echo "x is: 12345"
echo 'x is: 12345'
echo x\ is:\ 12345
echo "x is: $x"
echo 'x is:'\ "$x"
echo "$(echo "x ")is: $x"
# Assume there is no other file whose name begins with x in the current directory
touch "x is: $x"; echo x*
或者它可以是exec "echo", "x is: 12345"
Perl 或execlp("echo", "echo", "x is: 12345")
C 等语言。
这适用于每个命令。同样的原理也适用于其他 shell,尽管它们各自的扩展集略有(或显着)不同。
另一方面,选项由命令处理。例如,在ls -l -t
bash ls -l "-t"
(或任何类似的 shell)中,两者ls
以完全相同的方式运行,带有两个参数-l
和-t
。这就是为什么如果您想运行ls
以显示有关名为 .txt 的文件的信息,任何形式的 shell 引用都无法帮助您-t
。您可以使用 来做到这一点ls -l -- -t
,即通过--
作为参数传递。这是大多数命令遵循的约定:后面的任何内容都--
不会被解析为选项。再次强调,这是该命令的一个功能;对于 shell 来说,前导破折号没什么特别的。
一件可能变得棘手的事情是反斜杠。在 bash 和其他 shell 中,任何引号之外的反斜杠意味着下一个字符失去其特殊含义。例如,echo \"x\ is:\ 12345\"
打印"x is: 12345"
,因为前面有反斜杠的每个字符都失去了其特殊含义( 的引号语法"
,空格的单词分隔符)。但有些命令也解释反斜杠。当它们这样做时,您需要确保反斜杠到达它们。例如,在 bash 中,该echo
命令默认按字面打印反斜杠,但如果您传递选项-e
或shopt xpg_echo
先运行,则echo
有自己的转义序列(这是对于 bash,echo
不同 shell 和不同系统上的命令对于是否他们特别对待反斜杠)。例如,echo 'a\nb'
打印a\nb
时echo -e 'a\nb'
打印a
-newline-b
因为\n
意味着“换行”到echo -e
。另一方面,echo a\nb
打印anb
since\n
由shell扩展,意味着\
“引用下一个字符”。
另外,当涉及多个 shell 时(例如使用 SSH),请务必小心。当您ssh
在 shell 中运行时,本地 shell 像往常一样解析命令。 SSH 客户端将其非选项参数与中间的空格连接起来,并将结果字符串发送到 SSH 服务器。 SSH 服务器将此字符串传递到远程 shell。这意味着您在终端或 shell 脚本中键入的内容与在远程计算机上执行的内容之间存在两轮完整的 shell 扩展。
答案2
在您的示例中:shell 使用引号。
shell(所有 shell)采取的第一步是将命令行拆分为标记。
第一步第 7.3 章:命令行处理是分割。另请查看“图 7.1:命令行处理中的步骤”。
每个标记都是该行的一部分。每个令牌可能是一些扩展或其他代币。
分割主要是在元字符上完成的(空格是元字符,最常用),引用确实会修改分割。单引号将所有字符(包括空格)分组为一个标记。
您可以使用 printf“看到”拆分:
$ printf '<%s>\n' echo 'Hello World'
<echo>
<Hello World>
双引号也对字符进行分组,但允许一些内部扩展。
$ x=12345
$ echo "x is: $x"
x is: 12345
$ printf '%s' echo "x is: $x"
<echo>
<x is: 12345>
引号也可能显示为:
$ echo $(printf '"%s" ' "Hello World")
"Hello World"
然而,shell 进行的分裂增加了一个有趣的变化:
$ printf '<%s>\n' echo $(printf '"%s" ' "Hello World")
<echo>
<"Hello>
<World">
分割是根据命令扩展的结果完成的。引用命令扩展可以避免拆分:
$ printf '<%s>\n' echo "$(printf '"%s" ' "Hello World")"
<echo>
<"Hello World" >
引用的另一个来源是反斜杠。引用空格使其充当普通字符(而不是分割元字符):
$ printf '<%s>\n' echo Hello\ World
<echo>
<Hello World>