426

I want to assign the output of a command I run using os.system to a variable and prevent it from being output to the screen. But, in the below code ,the output is sent to the screen and the value printed for var is 0, which I guess signifies whether the command ran successfully or not. Is there any way to assign the command output to the variable and also stop it from being displayed on the screen?

var = os.system("cat /etc/services")
print var #Prints 0
Cajunluke
  • 3,103
  • 28
  • 28
John
  • 4,263
  • 3
  • 16
  • 4
  • 8
    Don't use `os.system` (nor `os.popen`, per the answer you accepted): use `subprocess.Popen`, it's _way_ better! – Alex Martelli Aug 17 '10 at 15:28
  • 1
    @AlexMartelli, one can't use a complex commands (e.g. piped) in subprocess.Popen(), but with os.system one can – vak May 25 '15 at 11:03
  • 2
    @vak, of course you can use pipes &c w/`subprocess.Popen` -- just add `shell=True`! – Alex Martelli May 26 '15 at 23:06
  • @AlexMartelli `shell=True` is (generally) a very bad idea! You have to be very sure of what you're executing :) – Ignacio Fernández Jul 21 '16 at 14:20
  • 3
    @AlexMartelli it's not useful to say that something is "way better" without saying why. – user650261 Mar 01 '22 at 16:25
  • @user650261, as is pretty obvious as soon as you look into os.system, os.popen, and subprocess.Popen, the latter gives you far more fine-grained control. That clearly will be "way better" if you need something different than the simpler approaches' behavior, as the OP evidently does. – Alex Martelli Mar 02 '22 at 17:16
  • @IgnacioFernández, os.open and os.popen require exactly the same degree of certainty about what you're executing as subprocess.Popen with shell=True, so that's absolutely no reason to avoid the latter. – Alex Martelli Mar 02 '22 at 17:18
  • @AlexMartelli this is Stack Overflow, where the goal is to provide that type of information to people asking questions rather than telling them to rtm. Even now, it is not clear what fine-grained control is more useful for OP's case. – user650261 Mar 03 '22 at 14:25

8 Answers8

641

From this question which I asked a long time ago, what you may want to use is popen:

os.popen('cat /etc/services').read()

From the docs for Python 3.6,

This is implemented using subprocess.Popen; see that class’s documentation for more powerful ways to manage and communicate with subprocesses.


Here's the corresponding code for subprocess:

import subprocess

proc = subprocess.Popen(["cat", "/etc/services"], stdout=subprocess.PIPE, shell=True)
(out, err) = proc.communicate()
print("program output:", out)
Sga
  • 3,608
  • 2
  • 36
  • 47
Chris Bunch
  • 87,773
  • 37
  • 126
  • 127
  • 7
    Note that Walter's `subprocess.check_output` solution is closer to the Pythonic one-liner it seems you're looking for, as long as you don't care about `stderr`. – chbrown Dec 10 '12 at 04:14
  • 13
    @ChrisBunch Why is suprocess a better solution than `os.popen`? – Martin Thoma Mar 29 '14 at 16:53
  • 6
    You should (almost) never use `shell=True`. See https://docs.python.org/3/library/subprocess.html#security-considerationshttps://docs.python.org/3/library/subprocess.html#security-considerations – dylnmc Sep 05 '14 at 20:27
  • what is the difference between os.Popen and subprocess.popen? – Bhavesh Munot Feb 02 '15 at 14:33
  • 1
    Good answer! Question though: Why in this example is `stdout=PIPE` specified, but not `stderr=PIPE` too? In example I see: `((out, err) = proc.communicate()` to capture both stdout and stderr, but if you don't use `stderr=PIPE`, will `err` ever get populated? Intentional or typo? – lostdorje Mar 27 '15 at 03:00
  • Is it a good idea to close this open pipe at any point? Or should we just leave it because it's not taking up an resources? – Tom Jun 29 '16 at 01:28
  • 63
    I keep coming across this and every time I wonder - why is it better to have three lines of complicated code rather than one line of obvious code? – Ant6n Jul 27 '16 at 14:36
  • 4
    Not only should you (almost) never use `shell=True`, but it's also incorrect in this case. `shell=True` makes the _shell_ the child process rather than `cat`, so `out` and `err` are the stdout/stderr of the _shell_ process rather than of the `cat` process – villapx Feb 22 '17 at 21:24
  • 1
    python retains the '\n' at the end so you probably want to strip that if you want exact compatability. – Jasen Jun 24 '18 at 23:47
  • How can we avoid to remove '\n'. I am facing this prob now – user3292373 Jul 27 '18 at 07:00
  • catching into variable seems to be more convenient in understanding in first glance like, **output = os.popen('ls').read()** – Mr. Suryaa Jha Oct 29 '18 at 14:58
  • @Ant6n is it better to try to slice a stick of butter with the flat side of a knife or the edge? Is it better to try to squish a stick of butter with the flat side of a knife, or the edge? Is it better to write two simple wrappers around a single, complex implementation of "knife", or is it better to create multiple implementations of the complex system, duplicating code and errors, and create a 1:1 mapping between simple interfaces at every level? -- This last question definitely gets a "no" -- the knife is an efficient construct, and the range of interactions is complex to specify. – Chris Oct 14 '20 at 15:03
  • Should add .rstrip() at the end to remove the extra line added by popen –  May 10 '21 at 20:15
  • If you want to execute a shell script with popen(), you must run the 'sh' command. E.g. subprocess.Popen(["/bin/sh", "/home/user/myscript", .....] – Andrew Fielden Jun 23 '22 at 14:34
234

You might also want to look at the subprocess module, which was built to replace the whole family of Python popen-type calls.

import subprocess
output = subprocess.check_output("cat /etc/services", shell=True)

The advantage it has is that there is a ton of flexibility with how you invoke commands, where the standard in/out/error streams are connected, etc.

Walter Mundt
  • 24,753
  • 5
  • 53
  • 61
  • 3
    note, `check_output` was added with python 2.7 https://docs.python.org/2.7/library/subprocess.html#subprocess.check_output – phyatt Nov 23 '15 at 21:06
  • 3
    Add `stderr=subprocess.STDOUT` to capture standard errors as well – Dolan Antenucci May 03 '19 at 18:33
  • 3
    i used this method with ***systeminfo*** command. Output was something ***nasty*** like this ```b'\r\nHost Name: DIMUTH-LAPTOP\r\nOS Name: Microsoft Windows 10 Pro\r\nOS Version: 10.0.10240 N/A Build 10240\r\nOS Manufacturer: Microsoft Corporation``` – ghost21blade Sep 25 '20 at 04:44
  • how can i print in line by line . because above output is a mess – ghost21blade Sep 25 '20 at 04:47
  • @ghost21blade found a solution? – otto Feb 12 '22 at 19:01
  • @otto Not yet !! – ghost21blade Mar 06 '22 at 07:36
  • 4
    @ghost21blade, you might want to look into this: `output.decode("utf-8").split("\n")` or whatever you´d like to do with it. You must convert it [into a string](https://stackoverflow.com/questions/606191/convert-bytes-to-a-string) first! – TrainedMusician Apr 23 '22 at 13:03
48

The commands module is a reasonably high-level way to do this:

import commands
status, output = commands.getstatusoutput("cat /etc/services")

status is 0, output is the contents of /etc/services.

ianmclaury
  • 1,156
  • 6
  • 5
  • 61
    Quoting from the [documentation of the commands module](http://docs.python.org/2/library/commands.html#commands): *"Deprecated since version 2.6: The commands module has been removed in Python 3. Use the subprocess module instead."*. – Cristian Ciupitu Apr 24 '13 at 14:12
  • 7
    sure it's outdated but sometimes you just want to get something done quickly and easily in a few lines of code. – fIwJlxSzApHEZIl Feb 28 '15 at 00:16
  • 7
    @advocate check out the check_output command of subprocess. It's quick, easy, and won't depreciate soon! – Luke Stanley Jul 04 '15 at 17:45
42

For python 3.5+ it is recommended that you use the run function from the subprocess module. This returns a CompletedProcess object, from which you can easily obtain the output as well as return code. Since you are only interested in the output, you can write a utility wrapper like this.

from subprocess import PIPE, run

def out(command):
    result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True)
    return result.stdout

my_output = out("echo hello world")
# Or
my_output = out(["echo", "hello world"])
Naofumi
  • 331
  • 4
  • 9
chtenb
  • 14,924
  • 14
  • 78
  • 116
  • Dont forget "capture_output=True" option for run() – Elysiumplain Jun 11 '20 at 07:27
  • 1
    Using: `result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True, capture_output=True)` with capture_output injected returns: `ValueError: stdout and stderr arguments may not be used with capture_output.` – Laice Apr 07 '21 at 11:21
34

I know this has already been answered, but I wanted to share a potentially better looking way to call Popen via the use of from x import x and functions:

from subprocess import PIPE, Popen


def cmdline(command):
    process = Popen(
        args=command,
        stdout=PIPE,
        shell=True
    )
    return process.communicate()[0]

print cmdline("cat /etc/services")
print cmdline('ls')
print cmdline('rpm -qa | grep "php"')
print cmdline('nslookup google.com')
Vasili Syrakis
  • 9,321
  • 1
  • 39
  • 56
  • 2
    3 years later, this is what worked for me. I threw this in a separate file called `cmd.py`, and then in my main file I wrote `from cmd import cmdline` and used it as needed. – Fares K. A. Jul 17 '17 at 13:18
  • 1
    I was getting the same issue with os.system returning 0, but I tried this method and it works great! And as a bonus it looks nice and tidy :) Thanks! – Sophie Muspratt Jan 17 '19 at 10:41
6

I do it with os.system temp file:

import tempfile, os

def readcmd(cmd):
    ftmp = tempfile.NamedTemporaryFile(suffix='.out', prefix='tmp', delete=False)
    fpath = ftmp.name
    if os.name=="nt":
        fpath = fpath.replace("/","\\") # forwin
    ftmp.close()
    os.system(cmd + " > " + fpath)
    data = ""
    with open(fpath, 'r') as file:
        data = file.read()
        file.close()
    os.remove(fpath)
    return data
vvvvv
  • 25,404
  • 19
  • 49
  • 81
lexa-b
  • 1,759
  • 15
  • 15
3

Python 2.6 and 3 specifically say to avoid using PIPE for stdout and stderr.

The correct way is

import subprocess

# must create a file object to store the output. Here we are getting
# the ssid we are connected to
outfile = open('/tmp/ssid', 'w');
status = subprocess.Popen(["iwgetid"], bufsize=0, stdout=outfile)
outfile.close()

# now operate on the file
Kearney Taaffe
  • 647
  • 8
  • 20
-1
from os import system, remove
from uuid import uuid4

def bash_(shell_command: str) -> tuple:
    """

    :param shell_command: your shell command
    :return: ( 1 | 0, stdout)
    """

    logfile: str = '/tmp/%s' % uuid4().hex
    err: int = system('%s &> %s' % (shell_command, logfile))
    out: str = open(logfile, 'r').read()
    remove(logfile)
    return err, out

# Example: 
print(bash_('cat /usr/bin/vi | wc -l'))
>>> (0, '3296\n')```
aveLestat
  • 413
  • 4
  • 7