如何在 bash 中用引号拆分字符串(如命令参数)?

如何在 bash 中用引号拆分字符串(如命令参数)?

我有一个这样的字符串:

"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"

我希望能够像这样拆分它:

aString that may haveSpaces IN IT
bar
foo
bamboo  
bam boo

我该怎么做?(最好使用一行代码)

答案1

当我看到 David Postill 的回答时,我想“一定有一个更简单的解决方案”。经过一些实验,我发现了以下方法:-

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
echo $string
eval 'for word in '$string'; do echo $word; done'

这是有效的,因为在执行结果行(即在线答案)之前eval扩展了该行(删除引号并扩展):string

for word in "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"; do echo $word; done

扩展至同一行的另一种方法是:

eval "for word in $string; do echo \$word; done"

这里string在双引号内扩展了,但必须对$进行转义,以便word在执行该行之前不会扩展(在另一种形式中,使用单引号具有相同的效果)。结果是:-

[~/]$ string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
[~/]$ echo $string
"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"
[~/]$ eval 'for word in '$string'; do echo $word; done'
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo
[~/]$ eval "for word in $string; do echo \$word; done"
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

答案2

最简单的解决方案是使用引用的参数创建一个数组,然后您可以根据需要循环该数组或直接将其传递给命令。

eval "array=($string)"

for arg in "${array[@]}"; do echo "$arg"; done   

附言:如果您找到更简单的方法,请发表评论eval

编辑:

基于@Hubbitus的回答,我们有一个完全净化和正确引用的版本。注意:这是矫枉过正,实际上会在大多数标点符号之前的双引号或单引号部分留下额外的反斜杠,但不会受到攻击。

declare -a "array=($( echo "$string" | sed 's/[][`~!@#$%^&*():;<>.,?/\|{}=+-]/\\&/g' ))"

我留给感兴趣的读者根据自己的需要进行修改http://ideone.com/FUTHhj

答案3

看起来 xargs 可以做得很好:

$ a='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
$ printf "%s" "$a" | xargs -n 1 printf "%s\n"
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

答案4

您也可以用declare而不是 来执行此操作eval,例如:

代替:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
echo "Initial string: $string"
eval 'for word in '$string'; do echo $word; done'

做:

declare -a "array=($string)"
for item in "${array[@]}"; do echo "[$item]"; done

但请注意,如果输入来自用户,则不会更安全!

因此,如果你尝试使用如下字符串:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`'

您会得到hostname评估(当然可能会是类似的rm -rf /)!

保护它的非常非常简单的尝试只是替换像 backtrick ` 和 $ 这样的字符:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`'
declare -a "array=( $(echo $string | tr '`$<>' '????') )"
for item in "${array[@]}"; do echo "[$item]"; done

现在你得到如下输出:

[aString that may haveSpaces IN IT]
[bar]
[foo]
[bamboo]
[bam boo]
[?hostname?]

您可能会在该好的答案中找到有关方法和优缺点的更多详细信息:https://stackoverflow.com/questions/17529220/why-should-eval-be-avoided-in-bash-and-what-should-i-use-instead/17529221#17529221

但仍然留下了攻击的载体。 我非常想在 bash 中使用像双引号 (") 这样的字符串引号方法,但不解释内容

相关内容