问题描述
我正在尝试解析一系列包含值和字符串混合的输出行。
我认为set
命令将是一种简单的方法。
初步测试似乎很有希望。这是示例命令行及其输出:
$ (set "one two" three; echo $1; echo $2; echo $3)
one two
three
很明显,我得到了两个变量,而第三个变量却什么也没有。
但是,当我将其放在脚本中(使用read
捕获输出行)时,会得到另一种解析:
echo \"one two\" three |
while read Line
do
echo $Line
set $Line
echo $1
echo $2
echo $3
done
这是输出:
"one two" three
"one
two"
three
echo $Line
命令显示引号在那里,但是set
命令不使用引号来分隔参数。为什么不呢?
在研究read
和while read
的用法时,我遇到了while IFS= read
这个习惯用法,因此我尝试了一下,但没有任何区别。
我已经阅读了许多有关报价的问题,但没有发现任何可以为我澄清的问题。显然,我的报价水平很混乱,但是在哪里呢?而我该怎么做才能得到我想要的东西,也就是在脚本中获得与从命令行获得的解析相同的解析?
谢谢。
解决方法
read
不解释引号,它只是将"one
读为一个标记,而将two"
读为另一个标记。 (请考虑一下如果外壳程序会评估随机位置的输入会导致出错的所有方式。Python2及其有缺陷的input()
的教训也是一个很好的例证。)
如果您真的想评估事物,eval
会这样做;但是它带来了一些警告,如果不小心的话,经常会导致安全问题。
取决于您要完成的工作,也许在单独的行中提供输入?或者,如果这些是用户提供的参数,则将其保留在"$@"
中。还请注意,如何将它们的子集传递给函数,如果您想弄乱它,它将获得自己的本地"$@"
。
(可能会因不引述echo
的参数而使自己感到困惑。请参阅When to wrap quotes around a shell variable。)
为什么不呢?
read
将输入拆分为IFS
中的每个 字符。使用未设置或默认的IFS
,即空格,制表符或换行符。其他任何字符都不是特殊字符,引号也不是特殊字符。
很明显,我的报价水平很混乱,但是在哪里?
您错误地认为read
足够聪明,可以解释引号。不是。此外,read
会忽略\
序列。阅读how to read a stream line by line和bash manual word splitting。
我该怎么做才能得到我想要的东西,即在脚本中获得与从命令行获得的解析相同的解析?
要获得与从命令行进行的相同分析,可以使用eval
。 eval
是邪恶的。 Eval command and security issues。
echo \"one two\" three |
while IFS= read -r line; do
eval "set $line" # SUPER VERY UNSAFE DO NOT USE
printf "%s\n" "$@"
done
使用eval
时,恶意用户可能会echo '"$(rm -rf *)"' | ...
立即删除您的文件。 Shell中最简单的解决方案是使用xargs
,它(在大多数情况下会令人困惑)在解析输入时包括引号解析。
echo \"one two\" three | xargs -n1 echo