【问题标题】:Unexpected strings escape in process argv进程 argv 中的意外字符串转义
【发布时间】:2015-10-10 00:08:12
【问题描述】:

有点惊讶:

$ node -p 'process.argv' $SHELL '$SHELL' \t '\t' '\\t'
[ 'node', '/bin/bash', '$SHELL', 't', '\\t', '\\\\t' ]

$ python -c 'import sys; print sys.argv' $SHELL '$SHELL' \t '\t' '\\t'
['-c', '/bin/bash', '$SHELL', 't', '\\t', '\\\\t']

预期与以下相同的行为:

$ echo $SHELL '$SHELL' \t '\t' '\\t'
/bin/bash $SHELL t \t \\t

这就是我需要传递这些东西的方式。

为什么在进程 argv 中使用 '\t''\\t' 进行额外转义?为什么处理方式与'$SHELL' 不同?这实际上来自哪里?为什么与 echo 行为不同?

首先,我认为这是minimist 部分的一些附加功能,但随后在裸Node.js 和Python 中得到了相同的结果。这里可能遗漏了一些明显的东西。

【问题讨论】:

  • 我猜您缺少的是字符串包含的内容与字符串在程序中的输入方式之间的区别。例如,您希望 Python 表达式 len("\n")len("\\n") 的值是什么?当您打印列表/数组时,python/node.js 会向您显示什么?我添加了更长的答案。

标签: bash shell argv


【解决方案1】:

在 BASH 中使用 $'...' 形式传递 \t\n\r\0 等转义序列:

python -c 'import sys; print sys.argv' $SHELL '$SHELL' \t $'\t' $'\\t'
['-c', '/bin/bash', '$SHELL', 't', '\t', '\\t']

根据man bash

$'string' 形式的单词被特殊处理。该单词扩展为字符串,并按照 ANSI C 标准的规定替换反斜杠转义字符。反斜杠转义序列(如果存在)按如下方式解码:

\a     alert (bell)
\b     backspace
\e
\E     an escape character
\f     form feed
\n     new line
\r     carriage return
\t     horizontal tab
\v     vertical tab
\\     backslash
\'     single quote
\"     double quote
\nnn   the eight-bit character whose value is the octal value nnn (one to three digits)
\xHH   the eight-bit character whose value is the hexadecimal value HH (one or two hex digits)
\uHHHH the Unicode (ISO/IEC 10646) character whose value is the hexadecimal value HHHH (one to four hex digits)
\UHHHHHHHH the Unicode (ISO/IEC 10646) character whose value is the hexadecimal value HHHHHHHH  (one  to  eight  hex digits)
\cx    a control-x character

【讨论】:

  • 谢谢,效果很好。请详细说明为什么会这样以及为什么它与echo 不同?我试图从中提炼一些通用规则。如果不是,我想就是这样:)
  • 其实echo也需要。在你的 shell 中首先输入echo 'foo\tbar',然后输入echo $'foo\tbar'。第一个将输出foo\tbar,第二个将输出foo bar,其中\t 被正确解释。我在答案中添加了相关的man bash 部分。
【解决方案2】:

在 python 和 node.js 中,print 处理标量字符串的方式与处理集合的方式之间存在差异。

字符串被简单地打印为一个字符序列。结果输出通常是用户期望看到的,但它不能用作字符串在语言中的表示。但是当一个列表/数组被打印出来时,你得到的是一个有效的列表/数组字面量,可以在程序中使用。

例如在python中:

>>> print("x")
x
>>> print(["x"])
['x']

打印字符串时,您只看到字符。但是在打印包含字符串的列表时,python 添加了引号字符,因此输出是有效的列表文字。同样,如有必要,它会添加反斜杠:

>>> print("\\")
\
>>> print(["\\"])
['\\']

node.js 的工作方式完全相同:

$ node -p '"\\"'
\
$ node -p '["\\"]'
[ '\\' ]

当您打印包含单个反斜杠的字符串时,您只会得到一个反斜杠。但是当你打印一个包含一个由单个反斜杠组成的字符串的列表/数组时,你会得到一个带引号的字符串,其中反斜杠用反斜杠转义,允许它在程序中用作文字。

与在 node 和 python 中打印字符串一样,标准的echo shell 实用程序只打印字符串中的实际字符。在标准 shell 中,没有类似于 node 和 python 打印数组的机制。然而,Bash 确实提供了一种机制,可以以可以用作 bash 程序一部分的格式打印出变量的值:

$ quote=\"
# $quote is a single character:
$ echo "${#quote}"
1
# $quote prints out as a single quote, as you would expect
$ echo "$quote"
"
# If you needed a representation, use the 'declare' builtin:
$ declare -p quote
declare -- quote="\""
# You can also use the "%q" printf format (a bash extension)
$ printf "%q\n" "$quote"
\"

(参考:declareprintf 上的 bash 手册。或者在 bash 会话中键入 help declarehelp printf。)


不过,这还不是全部。了解 shell 如何解释您键入的内容也很重要。换句话说,当你写

some_utility  \" "\"" '\"'

some_utility 在 argv 数组中实际看到了什么?

在标准 shell(包括 bash)中的大多数上下文中,C 风格的转义序列(如 \t)不会被这样解释。 (标准 shell 实用程序 printf 会在这些序列出现在格式字符串中时解释这些序列,并且一些其他标准实用程序也解释这些序列,但 shell 本身不会。)标准 shell 对反斜杠的处理取决于上下文:

  • 不带引号的字符串:反斜杠引用以下字符,无论它是什么(除非它是换行符,在这种情况下,反斜杠和换行符都从输入中删除)。

  • 双引号字符串:反斜杠可用于转义字符 $\"` ;此外,从输入中删除后跟换行符的反斜杠,就像在未引用的字符串中一样。在 bash 中,如果启用历史扩展(在 interactive shell 中默认情况下) , 也可以使用反斜杠来避免 ! 的历史扩展,但反斜杠保留在最后的字符串中。

  • 单引号字符串:反斜杠被视为普通字符。 (因此,无法在单引号字符串中包含单引号。)

Bash 添加了另外两种引用机制:

  • C 风格的引用,$'...'。如果单引号字符串前面有一个美元符号,则字符串 中的 C 样式转义序列会以与 C 编译器大致相同的方式解释。这包括标准空白字符,例如换行符 (\n)、八进制、十六进制和 Unicode 转义符(\010\x0a\u000A\U0000000A),以及一些非 C 序列,包括“控制”字符 (\cJ) 和 ESC 字符 \e\E(与 \x1b 相同)。反斜杠也可用于转义 \'"。(请注意,这是一个与双精度反斜杠字符列表不同的列表- 带引号的字符串;这里,美元符号或反引号前的反斜杠不是特殊的,而单引号前的反斜杠是特殊的;此外,不解释反斜杠换行序列。)

  • 特定于语言环境的翻译:$"..."。如果双引号字符串前面有美元符号,则反斜杠(以及变量扩展和命令替换)被解释为普通双引号字符串,然后在由当前语言环境确定的消息目录中查找该字符串。

(参考:Posix standardBash manual。)

【讨论】:

    猜你喜欢
    • 2012-12-01
    • 1970-01-01
    • 2012-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多