Home > Software engineering >  for loop in `Subprocess.run` results in `Syntax error: "do" unexpected`
for loop in `Subprocess.run` results in `Syntax error: "do" unexpected`

Time:03-15

I'm trying to run a for loop in a shell through python. os.popen runs it fine, but is deprecated on 3.x and I want the stderr. Following the highest-voted answer on How to use for loop in Subprocess.run command results in Syntax error: "do" unexpected, with which shellcheck concurs:

import subprocess
proc = subprocess.run(
    "bash for i in {1..3}; do echo ${i}; done",
    shell=True,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE, )

print(proc.stderr)

I'm ultimately trying to reset all usbs by calling this shell code https://unix.stackexchange.com/a/611305/362437 through python, so any alternate approaches to doing that would be appreciated too.

CodePudding user response:

When you do

subprocess.run('foo', shell=True)

it actually runs the equivalent of

/bin/sh -c 'foo'

(except that it magically gets all quotes right :-) ). So, in your case, it executes

/bin/sh -c "bash for i in {1..3}; do echo ${i}; done"

So the "command" given with the -c switch is actually a list of three commands: bash for i in {1..3}, do echo ${i}, and done. This is going to leave you with a very confused shell.

The easiest way of fixing this is probably to remove that bash from the beginning of the string. That way, the command passed to /bin/sh makes some sense.

If you want to run bash explicitly, you're probably better off using shell=False and using a list for the first argument to preserve your quoting sanity. Something like

import subprocess
proc = subprocess.run(
    ['/bin/bash', '-c', 'for i in {1..3}; do echo ${i}; done'],
    shell=False,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE, )
  • Related