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 )