Bash 中的嵌套引号

Nested quotes in Bash

请让我用一些比我的实际应用程序简单得多但表现出相同行为的示例脚本来解释我的场景:

对于演示,我使用了两个 bash shell 脚本。第一个叫做argtest.sh 简单的输出命令行参数:

#/bin/bash

echo "Argument 1: "
echo "Argument 2: "

测试:

root@test:~# ./argtest.sh 1 2
Argument 1: 1
Argument 2: 2

root@test:~# ./argtest.sh "1 2" 3
Argument 1: 1 2
Argument 2: 3

这按预期工作

我创建了另一个这样的命令:

./argtest.sh $(for i in ` seq 1 2 ` ; do echo Number $i; done) 

这是结果

Argument 1: Number
Argument 2: 1

我在 Number 周围尝试了许多不同的引号 (") 和转义引号 (\") 组合,但没有任何组合产生所需的输出:

Argument 1: Number 1
Argument 2: Number 2

我怎样才能得到那个输出?

你不能。命令替换受分词影响。它的输出是两行

Number 1
Number 2

但换行符在分词过程中被视为任意空格。因此,命令替换被分成 4 个词(Number1Number2),每个词都是 [=16] 的单独参数=].

在分词过程中,额外的引号将按字面处理;像 "a b" c 这样的字符串被分成 3 个单词("ab"c),而不是 2 个(a bc)。

要执行您想要的操作,您需要使用 while 循环从循环中一次读取一行输出。例如:

for i in $(seq 1 2); do
  echo "Number $i"
done | while read -r line; do
  ./argtest.sh "$line"
done

您可以使用xargs

for i in ` seq 1 2 ` ; do echo Number $i; done | xargs -d $'\n' ./argtest.sh

正如 chepner 所提到的,命令替换受到单词拆分的影响。

下面是几个例子,可以更好地理解这一点

这是我将 echo 替换为 printf 并转义双引号时的行为:-

for i in ` seq 1 2 ` ; do printf "\"Number %d\" " "$i"; done; printf "\n"
"Number 1" "Number 2"

./arg.sh  $(for i in ` seq 1 2 ` ; do printf "\"Number %d\" " "$i"; done; printf "\n")
Argument 1: "Number                                                                   
Argument 2: 1"     

现在,如果我尝试将整个命令替换用双引号引起来,它会将它们视为一个参数:-

./arg.sh "$(for i in ` seq 1 2 ` ; do printf "\"Number %d\" " "$i"; done; printf "\n")"
Argument 1: "Number 1" "Number 2"                                                                                                                         
Argument 2:                                                                                                                                                                      

因为参数受分词影响,您可以更改 $IFS 并将新分隔符放入 echo

IFS=$'_\n'
./argtest.sh $(for i in {1..2} ; do echo "Number ${i}_"; done )

不要忘记恢复旧的 $IFS

oIFS=$IFS
...
IFS=$oIFS

btw {1..2} 等同于 seq 1 2 但你不需要外部命令