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

时间:2013-03-07 14:57:29

标签: 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'

3 个答案:

答案 0 :(得分:2)

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

for i in 1
2
3 ; do echo  ; done

将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必须是bash之类的shell,因为POSIX sh不支持数组,正如jordanm所指出的那样。

答案 1 :(得分:2)

@chepner给了你正确的答案。

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

unset list
list=("A" "B" "C")
for i in `seq 1 ${#list[@]}` ; do echo $i ; done

答案 2 :(得分:0)

当您生成子shell时,将不会定义数组list,因此它不会产生长度:

for i in `seq 1 ${#list[@]}`

被视为:

for i in seq 1 0

见:

$ sh -c 'echo ${#list[@]}'
0

seq 1 0没有输出,因此看不到结果。

设置list并打印出当前shell中的每个值:

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

$ for i in `seq 1 ${#list[@]}`;do echo $i;done
1
2
3

或者在子shell中定义list

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