98

If I run echo a; echo b in bash the result will be that both commands are run. However if I use subprocess then the first command is run, printing out the whole of the rest of the line. The code below echos a; echo b instead of a b, how do I get it to run both commands?

import subprocess, shlex
def subprocess_cmd(command):
    process = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
    proc_stdout = process.communicate()[0].strip() 
    print proc_stdout
    
subprocess_cmd("echo a; echo b")
1

9 Answers 9

137

You have to use shell=True in subprocess and no shlex.split:

import subprocess

command = "echo a; echo b"

ret = subprocess.run(command, capture_output=True, shell=True)

# before Python 3.7:
# ret = subprocess.run(command, stdout=subprocess.PIPE, shell=True)

print(ret.stdout.decode())

returns:

a
b
Sign up to request clarification or add additional context in comments.

7 Comments

Ah I see, I had shell=True tested but command.split() was breaking it (list). Also to note to other people reading, using shell=True is a security hazard, make sure you trust the input.
You cannot use command.split() with shell=True. Actually the argument of subprocess.Popen with shell=True must be a string and not a list.
I found this to be a bit simpler and didn't have the garbling of the output: def do_shell(self, command): self.proc=subprocess.Popen(command,shell=True) self.proc.wait()
@Ravichandra: notice: bash in the title. On Windows, you probably want echo a & echo b command instead.
@Milan, on windows, the default shell is batch, which does not honor ';' as a separator.
|
35

I just stumbled on a situation where I needed to run a bunch of lines of bash code (not separated with semicolons) from within python. In this scenario the proposed solutions do not help. One approach would be to save a file and then run it with Popen, but it wasn't possible in my situation.

What I ended up doing is something like:

commands = '''
echo "a"
echo "b"
echo "c"
echo "d"
'''

process = subprocess.Popen('/bin/bash', stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
out, err = process.communicate(commands)
print(out)

So I first create the child bash process and after I tell it what to execute. This approach removes the limitations of passing the command directly to the Popen constructor.

The text=True addition is required for Python 3.

4 Comments

subprocess.check_output(commands, shell=True) works just fine. If there are bash-isms in commands then pass executable='/bin/bash'.
For Python 3 you might want to use: out, err = process.communicate(commands.encode('utf-8')) and print(out.decode('utf-8'))
If the commands are ; separated you might also want to add shell=True to the subprocess.Popen() line.
Modern Python should use subprocess.run(commands, shell=True). Like the Popen documentation already tells you, avoid it when you can, such as when all you want to do is run a subprocess and wait for it to finish.
23

Join commands with "&&".

os.system('echo a > outputa.txt && echo b > outputb.txt')

5 Comments

The second command would not run if the first command has an error. I would say use an ; instead.
The important part here is to use os.system() instead of subprocess.
os.system is not a substitute for subprocess. os.system cannot do all the things that subprocess does.
Maybe not, but it can echo just fine.
subprocess is preferred to os.system, but no one has given an answer that works with subprocess and shell=False (or analogous).
4

If you're only running the commands in one shot then you can just use subprocess.check_output convenience function:

def subprocess_cmd(command):
    output = subprocess.check_output(command, shell=True)
    print output

Comments

1
>>> command = "echo a; echo b"
>>> shlex.split(command);
    ['echo', 'a; echo', 'b']

so, the problem is shlex module do not handle ";"

2 Comments

command.split() gives ['echo', 'a;', 'echo', 'b'] and also fails. `
@bougui is right. Set "shell=True", the first argument of Popen need a command string like "echo a;echo b". and without "shell=True" the first argument of Popen should be a list, like:["echo","a"]
1

Got errors like when I used capture_output=True

TypeError: __init__() got an unexpected keyword argument 'capture_output'

After made changes like as below and its works fine

import subprocess

command = '''ls'''

result = subprocess.run(command, stdout=subprocess.PIPE,shell=True)

print(result.stdout.splitlines())

2 Comments

capture_output was only introduced in Python 3.7, which is quite old already, but apparently yours is older still.
(The shell=True is useless here; ls runs fine without a shell, though nominally you should then pass the command as a list ["ls"].)
0
import subprocess
cmd = "vsish -e ls /vmkModules/lsom/disks/  | cut -d '/' -f 1  | while read diskID  ; do echo $diskID; vsish -e cat /vmkModules/lsom/disks/$diskID/virstoStats | grep -iE 'Delete pending |trims currently queued' ;  echo '====================' ;done ;"


def subprocess_cmd(command):
    process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=True)
    proc_stdout = process.communicate()[0].strip()
    for line in proc_stdout.decode().split('\n'):
        print (line)

subprocess_cmd(cmd)

2 Comments

Welcome to StackOverflow! Please provide an explanation as to how the code you provided answers the original question.
Run commends with pipe(|)
0

Answering this old question for the sake of completeness, as none of the answers suggest splitting the commands nor give an answer that works with shell=False

Practical solution: two calls to subprocess.run or subprocess.Popen + Popen.communicate
Note this ignores stderr

import subprocess, shlex

proc1 = subprocess.Popen(shlex.split("echo a"), stdout=subprocess.PIPE)
(proc1_stdout, _) = process.communicate()
print(proc1_stdout, end="")
proc2 = subprocess.Popen(shlex.split("echo b"), stdout=subprocess.PIPE)
(proc2_stdout, _) = process.communicate()
print(proc2_stdout, end="")

This handles executing chained commands through a single subprocess.run call decently well without relying on shell=True. However, this is needlessly complicated

import shlex
import subprocess
import sys
import warnings

def subprocess_cmd(command, text=True, posix=False):
    tokens: shlex.shlex = shlex.shlex(command, posix=posix, punctuation_chars=True)
    commands: list[list[str]] = [[]]
    separators: list[str] = [";"]

    for token in tokens:
        if token in ("(", ")"):
            warnings.warn("Parentheses are not implemented and will be ignored", RuntimeWarning)
        if token in ("|", ";", "||", "&&"):
            separators.append(token)
            commands.append([])
        else:
            commands[-1].append(token)

    out: str|bytes = "" if text else b""
    err: str|bytes = "" if text else b""
    ret: int = 0

    for (command, separator) in zip(commands, separators):
        if (separator in (";")) or (separator in ("|", "||") and not ret == 0) or (separator in ("&&") and ret == 0):
            if separator != "|": print(out, end="")
            print(err, end="", file=sys.stderr)

            if separator == "|":
                result = subprocess.run(command, input=out, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=text)
            else:
                result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=text)

            out = result.stdout
            err = result.stderr
            ret = result.returncode

    print(out, end="")
    print(err, end="", file=sys.stderr)
    return ret

1 Comment

The boilerplate combo of Popen + communicate is unnecessary bloat. The standard library provides subprocess.run and the legacy subprocess.check_output and subprocess.check_command precisely so you don't need to write out this every time.
-2

What worked for me is very similar to what @Nico Schlömer and @bougui did.

But I had to add squared brackets to my commands and add comma in between each command.

import subprocess

command = ["echo a, echo b"]

after Python 3.7:

ret = subprocess.run(command, capture_output=True, shell=True)

before Python 3.7:

ret = subprocess.run(command, stdout=subprocess.PIPE, shell=True)


print(ret.stdout.splitlines())

1 Comment

This exhibits multiple misunderstandings of what the square brackets mean and what shell=True actually does. The comma is not a valid command separator in any standard shell so it's unclear under which circumstances this could ever have worked anywhere.

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.