Bash:将带引号的字符串拆分为数组

4

data.in:

a b c 'd e'

script.sh:

while read -a arr; do
    echo "${#arr[@]}"
    for i in "${arr[@]}"; do
        echo "$i"
    done
done

命令:

cat data.in | bash script.sh

输出:

5
a
b
c
'd
e'

问题:

如何将'd e'作为数组中的单个元素获取?


更新。这是我目前为止做得最好的:

while read line; do
    arr=()
    while read word; do
        arr+=("$word")
    done < <(echo "$line" | xargs -n 1)
    echo "${#arr[@]}"
    for i in "${arr[@]}"; do
        echo "$i"
    done
done

输出:

4
a
b
c
d e

然而,以下的data.in:
"a\"b" c

它将失败(以及到目前为止我发现的任何其他脚本,甚至是在重复的问题中):

xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option

但这个输入是合法的,因为你可以在命令行中输入:
echo "a\"b" c

它运行良好。因此,这是行为不匹配而不是非法输入。


2
这里的正确答案是使用 xargs printf '%s\0' 将您的字符串解析为一个以 NUL 为分隔符的流,这样 bash 就可以无歧义地读取它了。(xargs 在不使用 -d-0 扩展时,使用类似 shell 的解析规则将输入拆分成单词)。具体请参见:https://dev59.com/4l8e5IYBdhLWcg3wHXeJ#31485948。 - Charles Duffy
我认为这个问题在MCVE中更好地表达了问题,比它链接到的重复问题做得更好。对此给予+1的支持。此外,Charles,Tim Toady。如果这个问题仍然可以接受除你之外的答案,我会建议不同的路线。 - ghoti
@ghoti,嗯?我在这里并没有提供答案(只是评论,而且您完全可以自己提供),而且链接的问题确实是开放的。我很乐意在那里看到另一个正确的答案。 - Charles Duffy
@ghoti,...顺便提一下,我认为TIMTOWTDI是一种可怕的哲学。Pythonic方法是应该有“一种 -- 最好只有一种 -- 明显的方法来做到这一点”,这意味着惯用语的体量更小,因此在正确使用这些模式时,就不需要仔细审计边角情况。 (这是基于经验的抱怨:接手一个由具有不同惯用语体系的人编写的商业Perl代码库是我早期职业生涯中最不愉快的事件之一)。 - Charles Duffy
@CharlesDuffy 感谢您使用 -r。在重复的答案中,该解决方案适用于不同的工作,并且似乎无法与此处的 data.in 一起使用。我仍在测试代码,以查看是否存在其他可能导致其失败的边角情况。 - Cyker
显示剩余8条评论
1个回答

0
$ eval "a=($(cat data.in))"
$ for i in "${a[@]}";do echo "|$i|";done
|a|
|b|
|c|
|d e|
$

这很危险:如果你的 data.in 包含 $(rm -rf ~),那么肯定不希望执行它。 - Charles Duffy
顺便提一下,printf '|%s|\n' "${a[@]}" 可以在不使用循环的情况下以给定的形式打印出您的数组。 - Charles Duffy
另一个问题是,如果您的data.in中有一个*,那么您肯定希望输出中有一个*,而不是当前目录中的名称列表。 - Charles Duffy
是的。如果您不知道数据.in中可能存在什么,eval就有许多潜在的陷阱。 - Waxrat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接