milia
milia

Reputation: 541

Subprocess for loop fails with syntax error

I have a simple script in order to understand how one can run a shell for loop inside a subprocess call. I'm running it from GNU/Linux, inside a virtual enviroment and a BASH shell.

The script:

from subprocess import call

shellCommand = ['for','c','in','$(seq 1 10)','do','echo','$c','done']
call(shellCommand, shell=True)

And the error message:

c: 1: c: Syntax error: Bad for loop variable

What am I doing wrong here?

Upvotes: 0

Views: 126

Answers (2)

David Maze
David Maze

Reputation: 158898

I'd just do this sort of thing at the Python layer.

# for i in $(seq 10); do ...; done
for i in range(10):
  subprocess.call([...])

# for f in *.py; do ...; done
py_files = [f for f in os.listdir('.') where f.endswith('*.py')]
for f in py_files:
  subprocess.call([..., f, ...])

If at all possible you should avoid using shell=True because it is actually dangerous. Consider a filename or user input that contains a ; or a in it: how do you protect against it not doing what you expect (and how do you protect against a caller running any shell command on behalf of your process)? The array form avoids this problem.

Upvotes: 1

Martijn Pieters
Martijn Pieters

Reputation: 1121824

There are two problems:

  • Your shell syntax is simply not valid, you are missing key semicolons or newlines.
  • You can't pass in a complete shell script as separate arguments.

The correct syntax is:

for c in $(seq 1 10); do echo $c; done

Note the ; after the for ... in ... part before do, and another one after each command inside the do ... done block. You could also use newlines:

for c in $(seq 1 10)
do
    echo $c
done

Put your whole shell script in one argument; the argument is passed to sh -c ... and the -c switch expects the whole script in a single argument value:

shell_command = 'for c in $(seq 1 10); do echo $c; done'
call(shell_command, shell=True)

or, alternatively, using newlines:

shell_command = 'for c in $(seq 1 10)\ndo\n    echo $c\ndone'
call(shell_command, shell=True)

or

shell_command = '''
for c in $(seq 1 10)
do
    echo $c
done
'''
call(shell_command, shell=True)

Upvotes: 1

Related Questions