在里面重击陷阱在“Greg's Wiki”上我发现了以下引用:
事实上,
echo
这里使用该命令并不能绝对安全。-n
例如,如果变量包含,echo
将认为是一个选项,而不是要打印的数据。打印变量值的唯一绝对可靠的方法是使用printf
:printf "%s\n" "$foo"
但是当我输入:
var="-n Hello"
echo "$var"
它工作正常并且不会解释-n
为一个选项,所以我不明白为什么我引用的消息来源说我们需要使用它printf
才能绝对安全?
答案1
在您的示例中,它“偶然”正常工作。原因是这样的
- 您仅向 提供了一个参数
echo
,并且 - 您已按建议引用了变量。
这意味着echo
只将一个字符串视为命令行参数,即-n Hello
,包括“单词”之间的空格。现在,echo
(好吧,有些echo
s)被编程为它识别-n
为一个选项,但不是 string -n<space>Hello
,所以它会假设这是一个操作数- 即您想要“按原样”打印的字符串(好吧,可能会\
扩展序列)。
如果你用过
echo $var
如果没有引号,并且默认值为$IFS
,$var
则将在空格处拆分,并且echo
会看到二论点:-n
-它将解释为一个选项- 和Hello
。
这意味着在这种情况下,正确引用 shell 变量是防止无意中遇到echo
将操作数解释为选项的情况的补救措施。
然而:
请记住,链接源中的示例是通过“全局扩展”( ) 打印文件列表
*.zip
,其中变量必须不加引号使用,因此并不总是可以使用该保护。引用变量仅在以下情况下才有帮助:分词会导致
echo
看到一个选项参数。如果要打印的参数一-n
开始就是单个参数,则引用无法帮助您。例如,考虑这样一种情况,您收集了要打印的字符串数组
echo
,并且其中一个字符串恰好也是以下的选项参数echo
:arguments=( "-n" "Hello" )
然后,即使您正确引用该变量,如
echo "${arguments[@]}"
第一个标记
echo
看到的仍然是-n
它解释为选项,而不是操作数。如果您只是将多个变量作为参数传递给echo
,并且第一个变量是 或 开头,情况也是如此-n
。在这里,引用变量也无济于事。在一般情况下,如果程序接受以下形式的参数,即使引用也无济于事
-oValue
。作为假设的情况,采用一个
my_formula_renderer
排版数学公式并接受选项参数 的程序。然后,假设您想要排版公式,如-sfontsize
-sqrt(a^2 + b^2)
./my_formula_renderer "-sqrt(a^2+b^2)"
如果您的程序没有
--
结束选项解析的说明符,它会认为您尝试使用 的字体大小排版“无”qrt(a^2+b^2)
,而不是使用默认字体大小的上述公式。如果
-s
和其他公式元素之间有明确的空格,例如"-s +2t +u^2"
; ,它甚至没有帮助。它可能只是将其误解为+2t +u^2
带有前导空格的“字体大小”(请参阅例如这个问题这成为了一个问题)。如果程序接受单字母选项的“聚类”,情况也是如此。
正如 @Stéphane Chazelas 所指出的,
echo
例如,可以将字符串解释-neEenennne
为选项-n
、-e
和的串联-E
,因此尝试通过打印该字符串echo
可能会失败/以意想不到的方式表现。除了选项处理之外,
echo
(某些echo
s,包括启用选项bash
时的 s,xpg_echo
在某些版本中默认情况下)会将\n
、\\
等扩展\b
为换行符、反斜杠、退格字符,因此不能用于输出可能包含反斜杠的逐字字符串。
TL/DR
关键点是您引用的来源中的“绝对”一词,以及它echo
具体指代的事实。大多数情况下,echo
都会好的。但如果你开始盲目地依赖它,就会出现这样的情况(迟早)echo
,或者实际上是你调用的另一个更关键的程序,无意中将您打算作为“操作数”参数的内容误解为“选项”参数,通常当您将其作为 shell 变量传递时。