从文件中读取,然后使用 for 来分割字段,但保留引号

从文件中读取,然后使用 for 来分割字段,但保留引号

这是我试图在 bash shell 脚本中获得的内容:

for a in "1" "2 3" "4 5 6"
do
  echo "a: $a"
done
a: 1
a: 2 3
a: 4 5 6

但是当您使用变量时,引号的处理方式有所不同:

echo "\"1\" \"2 3\" \"4 5 6\"" > a.txt

cat a.txt
"1" "2 3" "4 5 6"

read aline < a.txt

如果你用双引号引用变量,你会得到这样的结果:

for b in "$aline"
do
  echo "b: $b"
done
b: "1" "2 3" "4 5 6"

如果没有引号,你会得到这个:

for b in $aline
do
  echo "b: $b"
done
b: "1"
b: "2
b: 3"
b: "4
b: 5
b: 6"

有没有办法让 for 处理变量的内容,如第一个示例中所示?

答案1

如果您同意在报价处理之上使用整个包(变量/算术/命令扩展、通配符等),则可以这样做:

echo '"a b" "x         y"' >a.txt

eval "set -- $(<a.txt)"
for b do
    printf 'b: %s\n' "$b"
done

b: a b
b: x         y

如果您希望它在常规 shell 中工作,而不仅仅是(或其他高级 shell,如或),请将$(<a.txt)其更改为。仅当您确定可以控制 的内容时才可以使用此选项,否则通过分割、命令扩展等方式利用您的脚本将变得微不足道。$(cat a.txt)bashzshksha.txt;

但是,如果您只想将这些参数传递给命令,则可以使用xargs,它只会处理引号,而不进行任何扩展:

xargs <a.txt printf 'b: %s\n'

xargs仅适用于外部命令,不适用于 shell 函数,将忽略双引号内的反斜杠,就像单引号内的反斜杠一样,并且无法解析多行字符串。

如果后面的限制没问题,您仍然可以xargs通过包装器或数组用作引号解析器:

mapfile -t args < <(xargs <a.txt printf '%s\n')
for b in "${args[@]}"; do
        printf 'b: %s\n' "$b"
done

答案2

如果bash您不使用ksh93ksh93shellbash无论如何都会尝试模仿),您可以使用-Sread内置选项来读取 csv(因此可以理解 csv 样式引用):

IFS=" " read -rSA array < a.txt
printf 'a: %s\n' "${array[@]}"

使用zsh,您可以使用它的z参数扩展标志,该标志用于标记变量的内容,就好像它是zsh代码一样(因此可以理解引用zsh,但不进行代码评估eval)以及要Q删除的参数扩展标志一层引用:

IFS= read -r line < a.txt
printf 'a: %s\n' "${(Q@)${(z)line}}"

@参数扩展标志是为了保留空元素,让人想起ksh${array[@]}行为)。

如果a.txt包含多行,则zsh可以在循环中使用and替换该read命令(如果带引号的字符串跨越多行,则每次调用可能会读取多行)。content=$(<a.txt)ksh93readwhileread

相关内容