进程 argv 中的意外字符串转义
Unexpected strings escape in process argv
有点惊讶:
$ 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 部分的一些 extras,但后来发现 Node.js 和 [=30= 都一样].这里可能遗漏了一些明显的东西。
使用$'...'
形式在BASH中传递转义序列,如\t
、\n
、\r
、[=16=]
等:
python -c 'import sys; print sys.argv' $SHELL '$SHELL' \t $'\t' $'\t'
['-c', '/bin/bash', '$SHELL', 't', '\t', '\t']
根据man bash
:
Words of the form $'string'
are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard. Backslash escape sequences, if present, are decoded as follows:
\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
在 python 和 node.js 中,print
使用标量字符串的方式与使用集合的方式不同。
字符串被简单地打印为字符序列。结果输出一般是用户期望看到的,但不能作为字符串在语言中的表示。但是当一个 list/array 被打印出来时,你得到的是一个有效的 list/array 文字,它可以在程序中使用。
例如,在python中:
>>> print("x")
x
>>> print(["x"])
['x']
打印字符串时,您只看到字符。但是当打印包含字符串的列表时,python 添加引号字符,因此输出是一个有效的列表文字。同样,如果需要,它会添加反斜杠:
>>> print("\")
\
>>> print(["\"])
['\']
node.js 的工作方式完全相同:
$ node -p '"\"'
\
$ node -p '["\"]'
[ '\' ]
当您打印包含单个反斜杠的字符串时,您只会得到一个反斜杠。但是当你打印一个包含由单个反斜杠组成的字符串的 list/array 时,你会得到一个带引号的字符串,其中反斜杠用反斜杠转义,允许它在程序中用作文字。
与在节点和 python 中打印字符串一样,标准 echo
shell 实用程序只打印字符串中的实际字符。在标准 shell 中,没有类似于节点和 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"
\"
(参考:bash declare
and printf
手册。或在 bash 会话中键入 help declare
和 help printf
。)
不过,这还不是全部。了解 shell 如何解释您键入的内容也很重要。换句话说,当你写
some_utility \" "\"" '\"'
some_utility
实际上在 argv 数组中看到了什么?
在标准 shell(包括 bash)的大多数上下文中,像 \t
这样的 C 风格转义序列不会被这样解释。 (当这些序列出现在格式字符串中时,标准 shell 实用程序 printf
会解释这些序列,一些其他标准实用程序也会解释这些序列,但 shell 本身不会。)处理标准 shell 的反斜杠取决于上下文:
不带引号的字符串:反斜杠引用后面的字符,无论它是什么(除非它是换行符,在这种情况下反斜杠和换行符都从输入中删除)。
双引号字符串:反斜杠可以用来转义字符$,\,", `; 此外,从输入中删除了一个反斜杠后跟一个换行符,就像在一个不带引号的字符串中一样。在 bash 中,如果 history启用扩展(因为它在 interactive shells 中默认启用),反斜杠也可用于避免 ! 的历史扩展, 但反斜杠保留在最后的字符串中。
单引号字符串:反斜杠被视为普通字符。 (因此,无法在单引号字符串中包含单引号。)
Bash 增加了两个引用机制:
C 风格引用,$'...'
。如果单引号字符串前面有一个美元符号,那么字符串 中的 C 风格转义序列将被 以与 C 编译器大致相同的方式解释。这包括标准空白字符,例如换行符 (\n
)、八进制、十六进制和 Unicode 转义字符 (0
、\x0a
、\u000A
、\U0000000A
),以及一些非 C 序列,包括 "control" 字符(\cJ
)和 ESC 字符 \e
或 \E
(与 \x1b
相同)。反斜杠也可以用来转义\、'和"。(注意这是不同的list from the list of backslashable characters in double-quoted strings; 这里,美元符号或反引号前的反斜杠是 not 特殊的,而单引号前的反斜杠是特殊的;此外,不解释反斜杠换行序列。)
特定于语言环境的翻译:$"..."
。如果双引号字符串前面有美元符号,则反斜杠(以及变量扩展和命令替换)被解释为普通双引号字符串,然后在由当前语言环境确定的消息目录中查找该字符串。
(参考文献:Posix standard, Bash manual。)
有点惊讶:
$ 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 部分的一些 extras,但后来发现 Node.js 和 [=30= 都一样].这里可能遗漏了一些明显的东西。
使用$'...'
形式在BASH中传递转义序列,如\t
、\n
、\r
、[=16=]
等:
python -c 'import sys; print sys.argv' $SHELL '$SHELL' \t $'\t' $'\t'
['-c', '/bin/bash', '$SHELL', 't', '\t', '\t']
根据man bash
:
Words of the form
$'string'
are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard. Backslash escape sequences, if present, are decoded as follows:
\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
在 python 和 node.js 中,print
使用标量字符串的方式与使用集合的方式不同。
字符串被简单地打印为字符序列。结果输出一般是用户期望看到的,但不能作为字符串在语言中的表示。但是当一个 list/array 被打印出来时,你得到的是一个有效的 list/array 文字,它可以在程序中使用。
例如,在python中:
>>> print("x")
x
>>> print(["x"])
['x']
打印字符串时,您只看到字符。但是当打印包含字符串的列表时,python 添加引号字符,因此输出是一个有效的列表文字。同样,如果需要,它会添加反斜杠:
>>> print("\")
\
>>> print(["\"])
['\']
node.js 的工作方式完全相同:
$ node -p '"\"'
\
$ node -p '["\"]'
[ '\' ]
当您打印包含单个反斜杠的字符串时,您只会得到一个反斜杠。但是当你打印一个包含由单个反斜杠组成的字符串的 list/array 时,你会得到一个带引号的字符串,其中反斜杠用反斜杠转义,允许它在程序中用作文字。
与在节点和 python 中打印字符串一样,标准 echo
shell 实用程序只打印字符串中的实际字符。在标准 shell 中,没有类似于节点和 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"
\"
(参考:bash declare
and printf
手册。或在 bash 会话中键入 help declare
和 help printf
。)
不过,这还不是全部。了解 shell 如何解释您键入的内容也很重要。换句话说,当你写
some_utility \" "\"" '\"'
some_utility
实际上在 argv 数组中看到了什么?
在标准 shell(包括 bash)的大多数上下文中,像 \t
这样的 C 风格转义序列不会被这样解释。 (当这些序列出现在格式字符串中时,标准 shell 实用程序 printf
会解释这些序列,一些其他标准实用程序也会解释这些序列,但 shell 本身不会。)处理标准 shell 的反斜杠取决于上下文:
不带引号的字符串:反斜杠引用后面的字符,无论它是什么(除非它是换行符,在这种情况下反斜杠和换行符都从输入中删除)。
双引号字符串:反斜杠可以用来转义字符$,\,", `; 此外,从输入中删除了一个反斜杠后跟一个换行符,就像在一个不带引号的字符串中一样。在 bash 中,如果 history启用扩展(因为它在 interactive shells 中默认启用),反斜杠也可用于避免 ! 的历史扩展, 但反斜杠保留在最后的字符串中。
单引号字符串:反斜杠被视为普通字符。 (因此,无法在单引号字符串中包含单引号。)
Bash 增加了两个引用机制:
C 风格引用,
$'...'
。如果单引号字符串前面有一个美元符号,那么字符串 中的 C 风格转义序列将被 以与 C 编译器大致相同的方式解释。这包括标准空白字符,例如换行符 (\n
)、八进制、十六进制和 Unicode 转义字符 (0
、\x0a
、\u000A
、\U0000000A
),以及一些非 C 序列,包括 "control" 字符(\cJ
)和 ESC 字符\e
或\E
(与\x1b
相同)。反斜杠也可以用来转义\、'和"。(注意这是不同的list from the list of backslashable characters in double-quoted strings; 这里,美元符号或反引号前的反斜杠是 not 特殊的,而单引号前的反斜杠是特殊的;此外,不解释反斜杠换行序列。)特定于语言环境的翻译:
$"..."
。如果双引号字符串前面有美元符号,则反斜杠(以及变量扩展和命令替换)被解释为普通双引号字符串,然后在由当前语言环境确定的消息目录中查找该字符串。
(参考文献:Posix standard, Bash manual。)