3

说我有文件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,但这是一行,因此似乎无济于事。

4

5 回答 5

2

这是一个可以完成这项工作的函数。对于大字符串来说它可能会很慢,但可以很好地完成工作,没有像任意代码执行或路径名扩展这样的警告:

#!/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

这不是一个令人满意的答案,但我怀疑是否有任何(安全)方法不会显式解析字符串。

于 2014-12-27T11:43:21.290 回答
1

此解决方案类似于 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 中创建的任何变量这一事实有关。

相关资源:

“我在管道中的循环中设置变量。为什么它们在循环终止后消失?或者,为什么我不能通过管道读取数据?”

有趣的。

于 2014-12-27T13:21:24.570 回答
1

IFS不会有帮助的。这有效:

eval bah=(`cat foo.txt`)

测试:

for i in "${bah[@]}"; do echo $i; done
于 2014-12-27T08:25:26.410 回答
1

使用 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)
于 2014-12-27T10:31:57.540 回答
0
$ . <(sed 's/^/set /' foo.txt)

$ echo $1
The

$ echo $2
quick brown
于 2014-12-27T06:41:42.410 回答