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