这是我试图在 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)
bash
zsh
ksh
a.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
您不使用ksh93
(ksh93
shellbash
无论如何都会尝试模仿),您可以使用-S
其read
内置选项来读取 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)
ksh93
read
while
read