Search code examples
bashloopsshsubshell

error message when using loop in shell command


What I'm doing wrong?

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

it should return:

1
2
3

instead of:

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

Solution

  • In your original code, the double-quoted string you pass to /bin/sh is processed by the shell before calling sh. In particular, both the command substitution involving seq and the parameter $i are expanded. As a result, you get the following (you can confirm by calling set -x before running your code):

    for i in 1
    2
    3 ; do echo  ; done
    

    The newline following 1 is treated as a termination of the sequence that for will iterate over. At this point, sh will expect the keyword do, but it instead sees 2.

    One way to fix this, assuming you really need to pass a string to another instance of the shell, is to tell seq to use a different separator (such as a space). You'll need to escape the dollar sign in $i as well so that it is evaluated by the subshell, not the current shell.

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

    This will work with sh, because the subshell does not see the array, which it would not understand anyway. sudo_O provides a different answer, which involves protecting the entire string passed to sh, and including the definition of list in that string so that it is visible in the subshell. Note that your sh must be a shell such as bash, since POSIX sh does not support arrays, as pointed out by jordanm.