在没有引号的情况下运行echo是否有危险?


11

我见过几个类似的主题,但是它们是指不引用变量,我知道这可能会导致不想要的结果。

我看到了这段代码,想知道是否可以在执行以下代码行时注入一些要运行的东西:

echo run after_bundle


我在遇到以下问题时遇到了这个问题:target =“ *** LIVE SERVER ***”; 回声目标:$目标; 和***扩展为文件夹列表... folder
Matt Parkins

Answers:


17

对于特定情况

echo run after_bundle

不需要报价。不需要引号,因为to的参数echo是不包含变量扩展或命令替换等内容的静态字符串。它们是“仅两个单词”(并且正如Stéphane所指出的,它们另外是在可移植字符集中构造的)。

当您处理shell可能会扩展或解释的可变数据时,就会出现“危险”。在这种情况下,必须注意外壳会做正确的事情,并且结果是预期的结果。

以下两个问题包含与此有关的信息:


echo有时用于“保护”此站点答案中可能有害的命令。例如,我可能会展示如何使用以下方法删除文件或将文件移动到新的目标位置

echo rm "${name##*/}.txt"

要么

echo mv "$name" "/new_dir/$newname"

这将在终端上输出命令,而不是实际删除或重命名文件。然后,用户可以检查命令,确定它们看起来不错,删除该命令echo并再次运行。

您的命令echo run after_bundle可能是对用户的指令,也可能是一段“注释掉”的代码,在不知道后果的情况下太危险了,无法运行。

使用echo这样的,人们必须知道什么修改命令功能和一个必须保证修改后的命令实际上安全的(它可能会没有,如果它包含重定向,并使用它在管道不工作等)


但是,添加引号不足以知道shell会做什么,就像您无法分辨出shell与壳有什么echo rm "first file.txt" "second file.txt"不同一样echo rm "first" "file.txt" "second" "file.txt",两者的输出是相同的。如果要生成一个shell命令作为输出,则必须使用printf '%q ' rm "first file.txt" "second file.txt"; echo或等效的命令来重新生成评估为argv传递的语法引用。
查尔斯·达菲

@CharlesDuffy我真的希望没人复制粘贴调试输出并在shell中运行它!
库萨兰达

1
生成shell命令,然后将它们传递到管道sh上并不是一种很常见的模式,并且看到人们问到“为什么foo我在命令行上运行它为什么能起作用,但是echo在该行前面发出该确切字符串的脚本却不会呢? ” 一直在这里发生。更重要的是,如果调试输出隐藏了您的错误,并且对您的错误与引用有关,则调试输出将无用echo
查尔斯·达菲

27

只需在@Kusalananda的最佳答案上额外说明一下。

echo run after_bundle

很好,因为传递的这3个参数¹中的任何字符都不echo包含shell专用的字符。

而且(我想在这里加一点)没有系统区域设置,这些字节可以转换为外壳程序专用的字符。

所有这些字符都在POSIX所谓的可移植字符集中。这些字符应在POSIX系统²上的所有字符集中都存在并进行相同的编码。

因此,无论使用哪种语言环境,该命令行都将被解释为相同。

现在,如果我们开始使用该可移植字符集之外的字符,即使它们不是外壳程序专用的字符,也最好对其进行引用,因为在另一个语言环境中,构成它们的字节可能会解释为可能会变成不同字符的字符。特殊的外壳。请注意,无论您使用的是echo任何命令还是其他命令,问题都不echo在于shell如何解析其代码。

例如在UTF-8中:

echo voilà | iconv -f UTF-8 -t //TRANSLIT

à被编码为0xc3 0XA0。现在,如果您在Shell脚本中有该行代码,并且使用使用字符集不是UTF-8的语言环境的用户调用了Shell脚本,那么这两个字节可能会产生非常不同的字符。

例如,在一个fr_FR.ISO8859-15语言环境中,一个典型的法语语言环境使用覆盖法语的标准单字节字符集(与大多数西欧语言(包括英语)相同),将0xc3字节解释为Ã字符,将0xa0 解释为非字符。破空格符。

在NetBSD³等少数系统上,不间断空格被视为空白字符(isblank()返回true时,它由匹配[[:blank:]]),bash因此像shell这样的外壳将其视为其语法中的标记定界符。

这意味着他们没有echo$'voil\xc3\xa0'as参数运行,而是以as参数运行$'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已运行)。

因此,我的建议是引用所有包含可移植字符集之外的字符的字符串。

但是请注意,由于编码\`在一些这些字符的编码被发现,最好不要使用\"..."$'...'(在其内部`和/或\仍是特殊的),但'...'不是到便携式字符集以外的引号字符。

我不知道任何具有语言环境的系统,其中字符集具有任何字符('当然,除了字符本身),其编码包含的编码',因此,这些'...'绝对应该是最安全的。

请注意,一些外壳程序还支持一种$'\uXXXX'表示法,用于根据其Unicode代码点表示字符。在像zsh和这样的shell中bash,字符以编码方式插入到语言环境的字符集中(尽管如果该字符集没有该字符,则可能导致意外的行为)。这样可以避免在外壳程序代码中插入非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'

(这可能会过大,但是如果您不确定便携式字符集中的字符,可以让您高枕无忧)

另外,请确保不要使用古老`...`的命令替换形式(这会引入反斜杠处理的另一个层次),而应使用$(...)替代形式。


¹从技术上讲,echo它也作为参数传递给echo实用程序(以告知实用程序如何调用),它是argv[0]and argc为3,尽管现今在大多数shell中echo都是内置的,因此,由3个参数exec()组成的/bin/echo文件的文件模拟为贝壳。将参数列表以第二个参数(argv[1]to argv[argc - 1])开头也很常见,因为这是命令主要作用的参数。

²一个明显的例外到是可笑ja_JP.SJIS的FreeBSD系统的区域,其字符集没有\,也没有~性格!

³请注意,尽管许多系统(例如FreeBSD,Solaris,而不是GNU)[[:blank:]]在UTF-8语言环境中都将U + 00A0视为a ,但在其他语言环境(例如使用ISO8859-15的语言环境)中却很少这样做,可能是为了避免此类问题。


在您的第一段中,您告诉我们“ ...传递给echo...的那3个参数中的字符...”,我只计算传递给命令的2个参数echo,我可以计算的参数是runafter_bundle,请仔细说明一下计数并得到3个论点?
Ferrybig '18年

1
@ViktorFonic,请参阅有关参数数量的编辑(并且主要问题不在echo)。有关(exec -a foo /bin/echo --help)如何将任意的第一个参数传递给/bin/echo实用程序的信息,请参见GNU系统和GNU Shell 。
斯特凡Chazelas

@Ferrybig参见Stephane的编辑,脚注1。以C语言常用的命令参数是参数数组,而argv [0]本身就是可执行文件名。类似于$0和壳中的位置参数。
Sergiy Kolodyazhnyy

有373编码在iconv其中ESC被转化成'。试试(作为一个例子):printf '\x1b'|iconv -f utf8 -t IBM-937|xxd
以撒

共有173种编码,其中某些代码点(ESC除外)被转换为'。尝试printf '\u2804' | iconv -f utf8 -t BRF | xxd。在某些编码中,有许多代码点成为'。UCS-4中的大约8695个代码点变为'。尝试printf '\U627' | iconv -cf utf-8 -t UCS-4。几种(37)编码将字符0x127转换为a '。试试printf '\U127' | iconv -cf utf8 -t UCS2 |xxd
艾萨克(Isaac)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.