Sté*_*las 27
只是在@Kusalananda 的好答案之上的额外说明。
echo run after_bundle
Run Code Online (Sandbox Code Playgroud)
很好,因为这 3 个参数¹中的任何字符都没有传递给echo
包含 shell 特殊的字符。
并且(我想在这里提出的额外要点)没有系统语言环境可以将这些字节转换为 shell 的特殊字符。
所有这些字符都在POSIX 所谓的可移植字符集中。这些字符应该在 POSIX 系统²上的所有字符集中出现和编码相同。
因此,无论语言环境如何,该命令行都将被解释为相同的。
现在,如果我们开始使用可移植字符集之外的字符,即使它们不是 shell 所特有的,也要引用它们是个好主意,因为在另一个语言环境中,构成它们的字节可能会被解释为不同的字符,这些字符可能会变成特殊的外壳。请注意,无论您使用echo
的是命令还是任何其他命令,问题都不echo
在于 shell 如何解析其代码。
例如在 UTF-8 中:
echo voilà | iconv -f UTF-8 -t //TRANSLIT
Run Code Online (Sandbox Code Playgroud)
这à
被编码为0xc3 0XA0。现在,如果您在 shell 脚本中有这行代码,并且 shell 脚本是由使用字符集不是 UTF-8 的语言环境的用户调用的,那么这两个字节可能会产生非常不同的字符。
例如,在fr_FR.ISO8859-15
语言环境中,典型的法语语言环境使用涵盖法语的标准单字节字符集(与大多数西欧语言包括英语相同),0xc3 字节被解释为Ã
字符,0xa0被解释为非-打破空间字符。
在一些像 NetBSD³ 这样的系统上,不间断空格被认为是一个空白字符(isblank()
它返回 true,它被匹配[[:blank:]]
),bash
因此shell like将其视为语法中的标记分隔符。
这意味着他们不是echo
使用$'voil\xc3\xa0'
as 参数运行,而是使用as 参数运行它$'voil\xc3'
,这意味着它不会voilà
正确打印。
使用 BIG5、BIG5-HKSCS、GB18030、GBK 等中文字符集会变得更糟,这些字符集的编码包含与|
, `
,相同的编码\
(最坏的情况)(还有那个可笑的 SJIS,又名 Microsoft Kanji,除了它¥
不是\
,但仍然\
被大多数工具视为,因为它在那里被编码为 0x5c )。
例如,如果在zh_CN.gb18030
中文语言环境中,您编写如下脚本:
echo ? reboot
Run Code Online (Sandbox Code Playgroud)
该脚本将? reboot
在使用 GB18030 或 GBK 的区域设置中输出,? reboot
在使用 BIG5 或 BIG5-HKSCS 的区域设置中,但在使用 ASCII 的 C 区域设置或使用 ISO8859-15 或 UTF-8 的区域设置中,将导致reboot
运行,因为 GB18030 编码of?
是 0xd4 0x7c 并且 0x7c 是|
ASCII 中的编码,所以我们最终运行:
echo ?| reboot
Run Code Online (Sandbox Code Playgroud)
(那个 ? 代表然而 0xd4 字节在语言环境中呈现)。使用危害较小的uname
代替 的示例reboot
:
$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
\324| uname$
$ LC_ALL=C bash ./myscript | sed -n l
Linux$
Run Code Online (Sandbox Code Playgroud)
(uname
被运行)。
所以我的建议是引用所有包含可移植字符集之外的字符的字符串。
但是请注意,由于编码\
和`
在一些这些字符的编码被发现,最好不要使用\
或"..."
或$'...'
(在其内部`
和/或\
仍是特殊的),但'...'
不是到便携式字符集以外的引号字符。
我不知道任何具有区域设置的系统,其中字符集具有任何字符('
当然除了它本身),其编码包含 的编码'
,因此这些'...'
绝对应该是最安全的。
请注意,一些 shell 还支持$'\uXXXX'
基于字符的 Unicode 代码点表示字符的符号。在像zsh
和这样的shell 中bash
,字符被插入到语言环境的字符集中编码(但如果该字符集没有该字符,则可能会导致意外行为)。这使您可以避免在 shell 代码中插入非 ASCII 字符。
所以上面:
echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '? reboot'
Run Code Online (Sandbox Code Playgroud)
或者:
echo $'voil\u00e0'
echo $'\u8a5c reboot'
Run Code Online (Sandbox Code Playgroud)
(需要注意的是,在没有这些字符的语言环境中运行时,它可能会破坏脚本)。
或者更好,因为\
它也是特殊的echo
(或至少是某些 echo
实现,至少是 Unix 兼容的):
printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '? reboot'
Run Code Online (Sandbox Code Playgroud)
(请注意,\
的第一个参数也是特殊的printf
,因此最好避免使用非 ASCII 字符,以防它们可能包含 的编码\
)。
请注意,您还可以执行以下操作:
'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'
Run Code Online (Sandbox Code Playgroud)
(这有点矫枉过正,但如果您不确定可移植字符集中哪些字符,可以让您放心)
还要确保永远不要使用古老`...`
的命令替换形式(它引入了另一个级别的反斜杠处理),而是使用$(...)
。
¹技术上,echo
作为参数也传递给echo
实用程序(告诉它,它是如何调用),它的argv[0]
和argc
是3,虽然在大多数shell时下echo
的内置,这样exec()
一的/bin/echo
有3个参数列表文件由模拟贝壳。将参数列表视为从第二个 ( argv[1]
to argv[argc - 1]
)开始也很常见,因为这是命令主要作用的参数。
² 一个显着的例外ja_JP.SJIS
是 FreeBSD 系统的可笑语言环境,其字符集没有\
或~
字符!
³ 请注意,虽然许多系统(FreeBSD、Solaris,但不是 GNU 系统)将 U+00A0 视为[[:blank:]]
UTF-8 语言环境中的一个,但很少有人在其他语言环境(例如使用 ISO8859-15 的语言环境中)这样做,这可能是为了避免此类问题。
Kus*_*nda 19
针对具体情况
echo run after_bundle
Run Code Online (Sandbox Code Playgroud)
不需要引用。不需要引用,因为 to 的参数echo
是静态字符串,不包含变量扩展或命令替换等。它们“只是两个词”(正如Stéphane 指出的那样,它们是由可移植字符集额外构建的)。
当您处理 shell 可能会扩展或解释的可变数据时,“危险”就会出现。在这种情况下,必须注意 shell 做正确的事情并且结果是预期的。
以下两个问题包含与此相关的信息:
echo
有时用于“保护”本站点答案中可能有害的命令。例如,我可能会展示如何使用
echo rm "${name##*/}.txt"
Run Code Online (Sandbox Code Playgroud)
或者
echo mv "$name" "/new_dir/$newname"
Run Code Online (Sandbox Code Playgroud)
这将在终端上输出命令,而不是实际删除或重命名文件。然后用户可以检查命令,确定它们看起来没问题,删除echo
并再次运行。
你的命令echo run after_bundle
可能是给用户的指令,也可能是一段“注释掉”的代码,在不知道后果的情况下运行起来太危险了。
echo
像这样使用,必须知道修改后的命令做了什么,并且必须保证修改后的命令实际上是安全的(如果它包含重定向,则可能不会,并且在管道上使用它不起作用,等等)
归档时间: |
|
查看次数: |
7169 次 |
最近记录: |