137

I want to get the stdout in a variable after running the os.system call.

Lets take this line as an example:

batcmd="dir"
result = os.system(batcmd)

result will contain the error code (stderr 0 under Windows or 1 under some linux for the above example).

How can I get the stdout for the above command without using redirection in the executed command?

Eduard Florinescu
  • 16,747
  • 28
  • 113
  • 179
  • 11
    By using the `subprocess` module instead. `os.system` does *not* give you access to process input or output. – Martijn Pieters Sep 11 '13 at 10:55
  • @MartijnPieters is this the only way ? – Eduard Florinescu Sep 11 '13 at 10:56
  • 3
    No, but `subprocess` encapsulates the differences between Windows and POSIX systems much better than you could achieve. Why reinvent the wheel? – Martijn Pieters Sep 11 '13 at 10:58
  • 3
    Believe me, I've reinvented that wheel before. Coming from a C background, doing the good old fork/exec/pipe dance almost comes naturally, but @MartijnPieters is right, just use subprocess and be done with it. – izak Sep 11 '13 at 11:01
  • 1
    Python 2.7 added the [`subprocess.check_output()` function](http://docs.python.org/2/library/subprocess.html#subprocess.check_output), which could be enough for your needs, but otherwise both those targets are just about equivalent. – Martijn Pieters Sep 11 '13 at 11:19
  • @MartijnPieters Can you put that as an answer? – Eduard Florinescu Sep 11 '13 at 11:22
  • Plz take a look at the first answer of http://stackoverflow.com/questions/3503879/assign-output-of-os-system-to-a-variable-and-prevent-it-from-being-displayed-on. – oldmonk Feb 14 '14 at 21:01
  • The answers on that question are indeed much better. – Charles Duffy Mar 05 '20 at 03:19

6 Answers6

168

If all you need is the stdout output, then take a look at subprocess.check_output():

import subprocess

batcmd="dir"
result = subprocess.check_output(batcmd, shell=True)

Because you were using os.system(), you'd have to set shell=True to get the same behaviour. You do want to heed the security concerns about passing untrusted arguments to your shell.

If you need to capture stderr as well, simply add stderr=subprocess.STDOUT to the call:

result = subprocess.check_output([batcmd], stderr=subprocess.STDOUT)

to redirect the error output to the default output stream.

If you know that the output is text, add text=True to decode the returned bytes value with the platform default encoding; use encoding="..." instead if that codec is not correct for the data you receive.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • In Windows works only if the cmd.exe is ran `result = subprocess.check_output(["cmd.exe", batcmd])` – Eduard Florinescu Sep 11 '13 at 11:31
  • 3
    @EduardFlorinescu: Or use `shell=True`; this was mentioned in the `subprocess` documentation but I've made it explicit here. – Martijn Pieters Sep 11 '13 at 11:37
  • Is there a way to do this in Jupyter Notebook? Looks like it only return an empty string. – Louis Yang Mar 18 '20 at 01:17
  • @LouisYang: yes, this works in Jupyter notebooks too. But take into account that the current working directory of the Python subprocess may differ, you may want to set the `cwd` argument to `subprocess.check_output()`. – Martijn Pieters Mar 18 '20 at 11:30
25

These answers didn't work for me. I had to use the following:

import subprocess
p = subprocess.Popen(["pwd"], stdout=subprocess.PIPE)
out = p.stdout.read()
print out

Or as a function (using shell=True was required for me on Python 2.6.7 and check_output was not added until 2.7, making it unusable here):

def system_call(command):
    p = subprocess.Popen([command], stdout=subprocess.PIPE, shell=True)
    return p.stdout.read()
Community
  • 1
  • 1
Patrick
  • 1,091
  • 1
  • 14
  • 14
22
import subprocess
string="echo Hello world"
result=subprocess.getoutput(string)
print("result::: ",result)
Syed Wasim
  • 221
  • 2
  • 2
  • 7
    You may want to add an explanation on why this answer is an improvement on the current high ranking answer. If it's a new api, or something was deprecated, it's important so others can differentiate the pros and cons. – Randy Sep 12 '19 at 16:49
  • 2
    It should be mentioned that this does use legacy carry overs from the commands module from Python 2, however it is not marked as deprecated in the API documentation found here: https://docs.python.org/3/library/subprocess.html#subprocess.getoutput This helped me a ton because I had a process that is rather old, and doesn't return 0 on successful invocations, meaning check_output couldn't be used, and call() wasn't working as intended. Upvoted! – root Jun 25 '20 at 22:54
11

I had to use os.system, since subprocess was giving me a memory error for larger tasks. Reference for this problem here. So, in order to get the output of the os.system command I used this workaround:

import os

batcmd = 'dir'
result_code = os.system(batcmd + ' > output.txt')
if os.path.exists('output.txt'):
    fp = open('output.txt', "r")
    output = fp.read()
    fp.close()
    os.remove('output.txt')
    print(output)
Edhowler
  • 715
  • 8
  • 17
  • 1
    `os.system()` **also** uses `fork()`/`clone()` under the hood, so how is this any better? – Charles Duffy Mar 04 '20 at 01:23
  • All I know is that one method worked and the other didn't. I'm using ubuntu and this command: out = os.system('ffmpeg -i ' + converted_filename + ' -ss ' + str(thumbnail_frame_time) + ' -vf scale=254:152 -vframes 1 -vcodec png -an -y ' + file_name + '.png') – Edhowler Mar 04 '20 at 18:59
  • That's dangerously insecure. Use `shlex.quote()` (or its python 2.x equivalent `pipes.quote()`) on all your filenames before substituting them into UNIX commands, or someone who drops a file in your upload folder named `$(rm -rf ~)'$(rm -rf ~)'.mpg` can cause you to have a very bad day. – Charles Duffy Mar 04 '20 at 19:11
  • You are right, thanks. – Edhowler Mar 04 '20 at 19:17
8

I would like to expand on the Windows solution. Using IDLE with Python 2.7.5, When I run this code from file Expts.py:

import subprocess
r = subprocess.check_output('cmd.exe dir',shell=False) 
print r

...in the Python Shell, I ONLY get the output corresponding to "cmd.exe"; the "dir" part is ignored. HOWEVER, when I add a switch such as /K or /C ...

import subprocess
r = subprocess.check_output('cmd.exe /K dir',shell=False) 
print r

...then in the Python Shell, I get all that I expect including the directory listing. Woohoo !

Now, if I try any of those same things in DOS Python command window, without the switch, or with the /K switch, it appears to make the window hang because it is running a subprocess cmd.exe and it awaiting further input - type 'exit' then hit [enter] to release. But with the /K switch it works perfectly and returns you to the python prompt. Allrightee then.

Went a step further...I thought this was cool...When I instead do this in Expts.py:

import subprocess
r = subprocess.call("cmd.exe dir",shell=False) 
print r

...a new DOS window pops open and remains there displaying only the results of "cmd.exe" not of "dir". When I add the /C switch, the DOS window opens and closes very fast before I can see anything (as expected, because /C terminates when done). When I instead add the /K switch, the DOS window pops open and remain, AND I get all the output I expect including the directory listing.

If I try the same thing (subprocess.call instead of subprocess.check_output) from a DOS Python command window; all output is within the same window, there are no popup windows. Without the switch, again the "dir" part is ignored, AND the prompt changes from the python prompt to the DOS prompt (since a cmd.exe subprocess is running in python; again type 'exit' and you will revert to the python prompt). Adding the /K switch prints out the directory listing and changes the prompt from python to DOS since /K does not terminate the subprocess. Changing the switch to /C gives us all the output expected AND returns to the python prompt since the subprocess terminates in accordance with /C.

Sorry for the long-winded response, but I am frustrated on this board with the many terse 'answers' which at best don't work (seems because they are not tested - like Eduard F's response above mine which is missing the switch) or worse, are so terse that they don't help much at all (e.g., 'try subprocess instead of os.system' ... yeah, OK, now what ??). In contrast, I have provided solutions which I tested, and showed how there are subtle differences between them. Took a lot of time but... Hope this helps.

LateNiteOwl
  • 101
  • 1
  • 6
  • Your solution (subprocess.call("cmd.exe dir",shell=False) )is work under ubuntu 12.04 except shell=True. Thanks! – JohnnyLinTW Aug 08 '16 at 09:44
5

commands also works.

import commands
batcmd = "dir"
result = commands.getoutput(batcmd)
print result

It works on linux, python 2.7.

buxizhizhoum
  • 1,719
  • 1
  • 23
  • 32