Arrays 将带空格的引用项读入数组
假设我有文件Arrays 将带空格的引用项读入数组,arrays,bash,Arrays,Bash,假设我有文件foo.txt "The" "quick brown" "fox" "jumps over" "the" "lazy dog." 我想将这些“字段”从文件读入数组。但是,如果字段有空格,我的尝试将失败 $ read -a bar < foo.txt $ echo ${bar[0]} "The" $ echo ${bar[1]} "quick $read-a条
foo.txt
"The" "quick brown" "fox" "jumps over" "the" "lazy dog."
我想将这些“字段”从文件读入数组。但是,如果字段有空格,我的尝试将失败
$ read -a bar < foo.txt
$ echo ${bar[0]}
"The"
$ echo ${bar[1]}
"quick
$read-a条
我看到一些答案建议更改
IFS
,但这是一行,因此似乎没有帮助。$。IFS
没有帮助。这是有效的:
$ . <(sed 's/^/set /' foo.txt)
$ echo $1
The
$ echo $2
quick brown
eval bah=(`cat foo.txt`)
测试:
使用Perl:
IFS=$'\n' a=( $(perl -ne '@a = split (/("[^"]*")/); for (my $i=1; $i<@a; $i+=2) { print "$a[$i]\n" }' foo.txt) )
下面是一个可以完成此任务的函数。对于大字符串来说,它可能会很慢,但可以很好地完成此任务,并且不会出现诸如任意代码执行或路径名扩展之类的警告:
#!/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
IFS=read-r行&2“分析%s处的字符串时出错\n”“$parse quoted\u items\u error”
出口1
fi
这不是一个令人满意的答案,但我怀疑是否有任何(安全的)方法不显式解析字符串。此解决方案类似于Håkon Hægland的:
它还使用Bash和,但是Perl部分稍微短一点
readarray -t words < <(cat fox.txt | perl -i -pe 's/(?<=") (?=")/\n/g')
readarray-t words<“我看到一些答案建议更改IFS
…”那么你一开始就严重误解了这些问题的含义。如果你想将这些值放入数组:。但是你会破坏你的位置参数。这可能会受到任意代码执行的影响:“quick$(ls)”“
。这可能会受到任意代码执行、路径名扩展和其他警告的影响,例如,使用类似于“The quick*”“brown$(ls)”[10]=lol
@gniourf_gniourf的字符串,这是正确的。如果您不信任文件的源,例如,您没有生成它,那么您必须首先解析它。这仍然是最简单、最直接的解决方案,因此值得注意≥4您可以将IFS=$'\n'a=($(perl…)
替换为mapfile-ta<@gniourf\u gniourf谢谢,答案已更新。。我认为文件名扩展在这两种情况下都不是这样。@HåkonHægland,您可以看看我的解决方案。;)我花了很长时间才弄明白如何正确重定向输出,但它与您的非常相似。在花了这么多时间找出管道的毛病之后,我发现放弃一切太糟糕了。你真的在hello周围加了引号吗?该文件需要被称为“hello”
(包括引号)。另外(单独)您可以尝试使用shopt-s nullglob
或shopt-s failglob
@gniourf\u gniourf是的,您是对的。。这很有趣。如果文件名中有诸如“hello”
之类的引号,则会展开*
。然后我今天也学到了一些东西:)谢谢。很好。。关于cat
和管道的使用:没有必要,使用readarray-ta<您的注释可能完全错误:如果您使用perl…|mapfile
然后mapfile
将在子shell中运行,因此将仅为此子shell设置数组。当命令退出并且子shell关闭时,一切都消失了!这就是为什么您需要流程替换。另见。顺便说一句,您的-n0
是无用的,但是您的命令缺少-t
。您是对的-t
是删除换行符的一个好选项<代码>-n0
是一种货物崇拜感谢您指出为什么变量是空的,并且它与缓冲无关。我会更新我的帖子!
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
readarray -t words < <(cat fox.txt | perl -i -pe 's/(?<=") (?=")/\n/g')
readarray -t words < <( perl -pe 's/(?<=") (?=")/\n/g' fox.txt )