说我有文件foo.txt
"The" "quick brown" "fox" "jumps over" "the" "lazy dog."
我想将文件中的这些“字段”读入一个数组。但是,如果该字段有空格,我的尝试将失败
$ read -a bar < foo.txt
$ echo ${bar[0]}
"The"
$ echo ${bar[1]}
"quick
我已经看到答案建议更改IFS
,但这是一行,因此似乎无济于事。
这是一个可以完成这项工作的函数。对于大字符串来说它可能会很慢,但可以很好地完成工作,没有像任意代码执行或路径名扩展这样的警告:
#!/bin/bash
parse_quoted_items() {
# Return array is parse_quoted_items_ary
local line=$1
parse_quoted_items_ary=() parse_quoted_items_error=
while [[ $line ]]; do
if [[ $line =~ ^[[:space:]]*\"([^\"]*)\"([[:space:]]+.+|)[[:space:]]*$ ]]; then
parse_quoted_items_ary+=( "${BASH_REMATCH[1]}" )
line=${BASH_REMATCH[2]}
else
parse_quoted_items_error=$line
return 1
fi
done
}
然后你可以用作
IFS= read -r line < foo.txt
if parse_quoted_items "$line"; do
declare -p parse_quoted_items_ary
else
printf >&2 "There was an error parsing the string at %s\n" "$parse quoted_items_error"
exit 1
fi
这不是一个令人满意的答案,但我怀疑是否有任何(安全)方法不会显式解析字符串。
此解决方案类似于 Håkon Hægland 的:
它还使用 Bash 的进程替换和readarray
/mapfile
但 Perl 部分要短一些。
readarray -t words < <(cat fox.txt | perl -i -pe 's/(?<=") (?=")/\n/g')
要不就
readarray -t words < <( perl -pe 's/(?<=") (?=")/\n/g' fox.txt )
该行使用 Perl 替换处理,利用后向和前瞻来检测 a"
后面和另一个 后面的空格"
。这些空格被换行符替换,以允许readarray
将每一行读入数组words
。然后将此多行输出传递给readarray
,-t
将在将尾随换行符添加到数组之前将它们去除。
请注意,在测试这个时,我无法天真地将输出perl
直接传输到readarray
,它似乎从未将一行读入数组,因为它是空的。正如@gniourf_gniourf 指出的那样,这与右手程序在子shell 中启动“绑定”在该子shell 中创建的任何变量这一事实有关。
相关资源:
“我在管道中的循环中设置变量。为什么它们在循环终止后消失?或者,为什么我不能通过管道读取数据?”
有趣的。
IFS
不会有帮助的。这有效:
eval bah=(`cat foo.txt`)
测试:
for i in "${bah[@]}"; do echo $i; done
使用 Perl:
IFS=$'\n' a=( $(perl -ne '@a = split (/("[^"]*")/); for (my $i=1; $i<@a; $i+=2) { print "$a[$i]\n" }' foo.txt) )
注意:这将比纯 bash 解决方案慢,因为它必须启动 Perl 解释器。
更新:
对于 Bash 4+:要避免IFS
全局设置:
readarray -t a < <(perl -ne '@a = split (/("[^"]*")/); for (my $i=1; $i<@a; $i+=2) { print "$a[$i]\n" }' foo.txt)
$ . <(sed 's/^/set /' foo.txt)
$ echo $1
The
$ echo $2
quick brown