JoshuaF
JoshuaF

Reputation: 1215

for loop in `Subprocess.run` results in `Syntax error: "do" unexpected`

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.

Upvotes: 1

Views: 569

Answers (1)

Ture Pålsson
Ture Pålsson

Reputation: 6806

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, )

Upvotes: 4

Related Questions