0

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?

2
  • 4
    That's not valid shell syntax, no. This is not really a Python problem, the same command would fail when run from a shell directly. Commented Jul 27, 2018 at 10:56
  • 3
    And for shell commands, just pass in a single string, not a list: shell_command = 'for c in $(seq 1 10); do echo $c; done', then call(shell_command, shell=True)`. Note the semicolons I added. Commented Jul 27, 2018 at 10:58

2 Answers 2

1

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)
Sign up to request clarification or add additional context in comments.

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.