Bash 在shell命令中使用循环时出现错误消息

Bash 在shell命令中使用循环时出现错误消息,bash,loops,sh,subshell,Bash,Loops,Sh,Subshell,我做错了什么 unset list list=("A" "B" "C") /bin/sh -c "for i in `seq 1 ${#list[@]}` ; do echo $i ; done " 它应返回: 1 2 3 而不是: /bin/sh: -c: line 1: syntax error near unexpected token `2' /bin/sh: -c: line 1: `2' 在原始代码中,传递给/bin/sh的双引号字符串在调用sh之前由shell处理。特别是,涉

我做错了什么

unset list
list=("A" "B" "C")
/bin/sh -c "for i in `seq 1 ${#list[@]}` ; do echo $i ; done "
它应返回:

1
2
3
而不是:

/bin/sh: -c: line 1: syntax error near unexpected token `2'
/bin/sh: -c: line 1: `2'

在原始代码中,传递给
/bin/sh
的双引号字符串在调用
sh
之前由shell处理。特别是,涉及
seq
的命令替换和参数
$i
都被展开。因此,您会得到以下结果(您可以在运行代码之前通过调用
set-x
进行确认):

1后面的换行符被视为
for
将迭代的序列的终止符。此时,
sh
将期望关键字
do
,但它会看到
2

假设您确实需要将字符串传递给shell的另一个实例,解决此问题的一种方法是告诉
seq
使用不同的分隔符(例如空格)。你需要逃跑 美元符号
$i
,因此它是由子shell而不是当前shell计算的

unset list
list=("A" "B" "C")
/bin/sh -c "for i in `seq -s' ' 1 ${#list[@]}` ; do echo \$i ; done "

这将适用于
sh
,因为子shell没有看到数组,它无论如何也不会理解数组。sudo_O提供了一个不同的答案,它涉及到保护传递给
sh
的整个字符串,并在该字符串中包含
list
的定义,以便它在子shell中可见。请注意,您的
sh
必须是一个shell,例如
bash
,因为POSIX
sh
不支持数组,正如jordanm指出的那样。

在原始代码中,传递给
/bin/sh
的双引号字符串在调用
sh
之前由shell处理。特别是,涉及
seq
的命令替换和参数
$i
都被展开。因此,您会得到以下结果(您可以在运行代码之前通过调用
set-x
进行确认):

1后面的换行符被视为
for
将迭代的序列的终止符。此时,
sh
将期望关键字
do
,但它会看到
2

假设您确实需要将字符串传递给shell的另一个实例,解决此问题的一种方法是告诉
seq
使用不同的分隔符(例如空格)。你需要逃跑 美元符号
$i
,因此它是由子shell而不是当前shell计算的

unset list
list=("A" "B" "C")
/bin/sh -c "for i in `seq -s' ' 1 ${#list[@]}` ; do echo \$i ; done "

这将适用于
sh
,因为子shell没有看到数组,它无论如何也不会理解数组。sudo_O提供了一个不同的答案,它涉及到保护传递给
sh
的整个字符串,并在该字符串中包含
list
的定义,以便它在子shell中可见。请注意,您的
sh
必须是一个shell,例如
bash
,因为正如jordanm所指出的,POSIX
sh
不支持数组。

在生成子shell时,将不会定义数组
列表
,因此它没有长度:

for i in `seq 1 ${#list[@]}`
被视为:

for i in seq 1 0
见:

并且
seq 1 0
不产生任何输出,因此看不到任何结果

设置
列表
并打印当前shell中的每个值:

$ list=("A" "B" "C")

$ for i in `seq 1 ${#list[@]}`;do echo $i;done
1
2
3
或者在子shell中定义
列表

$ sh -c 'list=("A" "B" "C");for i in `seq 1 ${#list[@]}`;do echo $i;done'
1
2
3

生成子shell时,将不会定义数组
列表
,因此它将没有长度生成:

for i in `seq 1 ${#list[@]}`
被视为:

for i in seq 1 0
见:

并且
seq 1 0
不产生任何输出,因此看不到任何结果

设置
列表
并打印当前shell中的每个值:

$ list=("A" "B" "C")

$ for i in `seq 1 ${#list[@]}`;do echo $i;done
1
2
3
或者在子shell中定义
列表

$ sh -c 'list=("A" "B" "C");for i in `seq 1 ${#list[@]}`;do echo $i;done'
1
2
3

@切普纳给了你正确的答案

顺便说一下,您也可以像这样缩短命令(不再涉及shell)


@切普纳给了你正确的答案

顺便说一下,您也可以像这样缩短命令(不再涉及shell)


这不能回答他的问题。如前所述,他想知道为什么他看不到“1 2 3”,而不是“A B C”。@chepner你显然误解了打印
A B C
而不是
1 2 3
的要点,这不会使答案出错。我已经说得很清楚了。他的问题是关于语法错误,而不是如何打印“abc”。在编辑之前,这并没有回答这个问题。@chepner在编辑之前,问题是(现在仍然是)变量不会在子shell中定义,这才是真正的问题。它不需要在子shell中定义。他实际上并没有使用列表的内容,他只是需要长度,在调用子shell之前可以对长度进行评估。通过你的编辑,我实际上已经对你的答案投了更高的票。这并没有回答他的问题。如前所述,他想知道为什么他看不到“1 2 3”,而不是“A B C”。@chepner你显然误解了打印
A B C
而不是
1 2 3
的要点,这不会使答案出错。我已经说得很清楚了。他的问题是关于语法错误,而不是如何打印“abc”。在编辑之前,这并没有回答这个问题。@chepner在编辑之前,问题是(现在仍然是)变量不会在子shell中定义,这才是真正的问题。它不需要在子shell中定义。他实际上并没有使用列表的内容,他只是需要长度,在调用子shell之前可以对长度进行评估。通过你的编辑,我实际上已经对你的答案投了更高的票。你确定
sh
是bash吗?POSIX
sh
没有数组。您确定
sh
是bash吗?POSIX
sh
没有数组。我不确定OP是否有理由将命令传递给子shell,但这肯定更简单。如果在当前shell中,您不需要担心换行符,只需删除“-s”,我不确定OP是否有理由将命令传递给子shell,但这肯定更简单。如果你在当前的shell中,你不需要担心换行,所以你可以删除t