到目前为止,我一直在使用"\""
打印双引号:
$ x="abc def"
$ echo "x=\"$x\""
x="abc def"
然而,这种行为似乎是未定义的?
为了echo
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html(忽略符合 XSI 的系统的额外规范),反斜杠会导致完全未定义的行为:
要写入标准输出的字符串。如果第一个操作数是 -n,或者任何操作数包含 <反斜杠> 字符,则结果是实现定义的。
该页面为printf
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html主要指文件格式符号https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap05.html#tag_05这也不支持\"
.
下面两种方法怎么样?它们是否明确符合 POSIX 标准?
连接
'
和"
字符串$ x="abc def" $ printf x=%s\\n '"'"$x"'"' x="abc def"
使用八进制值
"
:$ x="abc def" $ printf x=\\42%s\\42\\n "$x" x="abc def"
有哪些替代方案?
答案1
是的,这种行为(使用\"
)在 POSIX 中对于命令 是未定义的echo
,而不是在任何地方和所有东西。这就是使用 printf 而不是 echo 的原因之一。请阅读为什么 printf 比 echo 更好?。
当然,这里有必要澄清一下。
A\"
是实现定义的仅当那是一个操作数回声。
单词定义在这里有多重要?因为我们在命令行写的东西都是被读取,多次修改,然后作为操作数给予实用程序。我们所说的命令行参数(“单词”)通过 shell 以多种方式进行转换,最后的更改之一是“引用删除”。如果在所有这些之后,结果操作数包含\"
,echo
有权执行任何它认为其“正确”的操作。
如果命令行是echo '\"'
orecho "\\\""
转换为\"
操作数,则结果是实现定义的。
你的例子x="abc def"; echo "x=\"$x\""
是不是实现定义的,因为 echo 接收的操作数是x="abc def"
。但这取决于变量的内容x
。
为避免此类可能的问题你应该使用 printf,它没有任何限制,因为无论操作数是什么,输出都是 POSIX 定义的。
因此:打印双引号的通常解决方案是将它们放在 printf 命令的格式部分中:
$ printf 'x="%s"\n' "$x"
x="abc def"
如果需要打印单引号,则必须更改引用,或者使用“退出引号 - 放置单引号 - 再次开始引用”的常见技巧:
$ printf 'x='\''%s'\''\n' "$x"
x='abc def'
或者:
$ printf "x='%s'"'\n' "$x"
x='abc def'
或者,更好的是,将单引号的负担切换到双引号字符串:
$ printf 'x=%s\n' "'$x'"
x='abc def'
但是,是的,由于 printf 支持\ddd
八ddd
进制数,您还可以使用:
$ printf 'x=\042%s\042\n' "$x"
x="abc def"
上述所有解决方案都是有效的 POSIX 结构,请选择一个。
答案2
考虑在 POSIX shell 中运行以下命令:
echo "abc"
根据中指定的规则2.2.3 双引号,"abc"
是双引号,因此双引号字符不是文字,而是双引号构造的封闭字符。因此echo
甚至不接收"abc"
作为其第一个参数值,而仅接收abc
.
此外,2.2.3 双引号的最后一段定义了双引号内出现的反斜杠的行为:对于"
和一些其他字符,它“应保留其作为转义字符的特殊含义”,这在 2.2 节中定义.1 转义字符。根据该部分,转义字符保留“后续字符的字面值”。
这意味着上面最初引发问题的原始问题文本中的第一个示例完全没问题:
$ x="abc def"
$ echo "x=\"$x\""
x="abc def"
该echo
程序甚至永远不会接收任何反斜杠字符,因为 POSIX shell 已经将其用作转义字符。因此,如果参数值中出现反斜杠,echo
规范(或任何其他程序)定义什么并不重要printf
,因为没有反斜杠。
当然,以上这些都是有限的,仅对x
上面设置的值(以及其他一些有限值)有效。如果 的值x
可以变成其他值,则可能会在删除引号后包含反斜杠\
,这是将 shell 命令行参数(shell 术语中的“word”)转换为操作数在关于实用程序的 POSIX 说法。
这使得接下来的内容echo
面临成为实现定义的风险;printf
没有这个问题。