191

From the examples in docs on subprocess.run() it seems like there shouldn't be any output from

subprocess.run(["ls", "-l"])  # doesn't capture output

However, when I try it in a python shell the listing gets printed. I wonder if this is the default behaviour and how to suppress the output of run().

10 Rep
  • 2,217
  • 7
  • 19
  • 33
planetp
  • 14,248
  • 20
  • 86
  • 160
  • 2
    http://stackoverflow.com/questions/8529390/is-there-a-quiet-version-of-subprocess-call – user2290362 Dec 15 '16 at 19:25
  • 3
    `subprocess.run()` doesn't capture stdout or stderr by default, to do so requires passing `PIPE`s for the `stdout` and/or `stderr` arguments (it's right in the linked documentation). So, unless you do, they will display as they normally would from the other process. – martineau Dec 15 '16 at 20:11
  • 3
    Do you want to suppress the output or capture it? – SethMMorton Dec 15 '16 at 20:27
  • @SethMMorton: right now I just need to hide it. – planetp Dec 15 '16 at 20:41
  • 16
    This question has been marked as a duplicate, but I think this is a mistake, because the API changed significantly between Python 2.7 (`process.call`), and Python 3.5 (`process.run`). I came here explicitly looking for the Python 3 answer to this question. The accepted answer for this question, and the top comment on it, are both more useful in that context than the answers on the other question. – GlennS Dec 17 '18 at 01:22
  • 11
    Since [Python 3.7](https://docs.python.org/3.7/library/subprocess.html) you can also just use the `capture_output=True` parameter. – Czechnology May 08 '19 at 16:42
  • 1
    Agree that this question shouldn't be marked as duplicated. `subprocess.run` is the only high-level API recommended in the subprocess module since Python 3.5. – Benjamin Du Aug 13 '19 at 21:24
  • What do y'all mean by capture? Where does the output go when I capture it? – Joooeey Sep 09 '21 at 08:06

2 Answers2

338

Suppressing

Here is how to suppress output, in order of decreasing levels of cleanliness. They assume you are on Python 3.

  1. You can redirect to the special subprocess.DEVNULL target.
import subprocess

# To redirect stdout (only):
subprocess.run(
    ['ls', '-l'],
    stdout = subprocess.DEVNULL
)

# to redirect stderr to /dev/null as well:
subprocess.run(
    ['ls', '-l'],
    stdout = subprocess.DEVNULL,
    stderr = subprocess.DEVNULL
)

# Alternatively, you can merge stderr and stdout streams and redirect
# the one stream to /dev/null
subprocess.run(
    ['ls', '-l'],
    stdout = subprocess.DEVNULL,
    stderr = subprocess.STDOUT
)
  1. If you want a fully manual method, can redirect to /dev/null by opening the file handle yourself. Everything else would be identical to method #1.
import os
import subprocess

with open(os.devnull, 'w') as devnull:
    subprocess.run(
        ['ls', '-l'],
        stdout = devnull
    )

Capturing

Here is how to capture output (to use later or parse), in order of decreasing levels of cleanliness. They assume you are on Python 3.

NOTE: The below examples use universal_newlines=True (Python <= 3.6).

  • This causes the STDOUT and STDERR to be captured as str instead of bytes.
    • Omit universal_newlines=True to get bytes data
  • Python >= 3.7 accepts text=True as a short form for universal_newlines=True
  1. If you simply want to capture both STDOUT and STDERR independently, AND you are on Python >= 3.7, use capture_output=True.
import subprocess

result = subprocess.run(
    ['ls', '-l'],
    capture_output = True, # Python >= 3.7 only
    text = True # Python >= 3.7 only
)
print(result.stdout)
print(result.stderr)
  1. You can use subprocess.PIPE to capture STDOUT and STDERR independently. This works on any version of Python that supports subprocess.run.
import subprocess

result = subprocess.run(
    ['ls', '-l'],
    stdout = subprocess.PIPE,
    universal_newlines = True # Python >= 3.7 also accepts "text=True"
)
print(result.stdout)

# To also capture stderr...
result = subprocess.run(
    ['ls', '-l'],
    stdout = subprocess.PIPE,
    stderr = subprocess.PIPE,
    universal_newlines = True # Python >= 3.7 also accepts "text=True"
)
print(result.stdout)
print(result.stderr)

# To mix stdout and stderr into a single string
result = subprocess.run(
    ['ls', '-l'],
    stdout = subprocess.PIPE,
    stderr = subprocess.STDOUT,
    universal_newlines = True # Python >= 3.7 also accepts "text=True"
)
print(result.stdout)
Skippy le Grand Gourou
  • 6,976
  • 4
  • 60
  • 76
SethMMorton
  • 45,752
  • 12
  • 65
  • 86
  • 31
    Note that from Python ver. 3.3 there is actually a `subprocess.DEVNULL`, so `stdout` argument can be assigned directly without the `open`, just using `stdout=subprocess.DEVNULL`. – EquipDev Mar 24 '17 at 14:09
  • I'm getting `NameError: name 'devnull' is not defined` on Python 3.7 –  Sep 21 '19 at 03:00
  • 1
    @Sabrina Have you defined `devnull`? – SethMMorton Sep 21 '19 at 03:05
  • Is it possible to print the output while simultaneously capturing the data in a pipe (to use later or parse)? – Christopher Sep 23 '19 at 15:51
  • 1
    @Christopher If you don't care about printing while the program is running, you can just print `result.stdout` after-the-fact. If you want to capture and print real-time, it's a bit more complicated because you cannot use `subprocess.run`, you need the lower-level `subprocess.Popen`. I have used something [like this answer](https://stackoverflow.com/a/6414278/1399279) when I need to do that (adjust syntax from answer for Python 3). – SethMMorton Sep 23 '19 at 20:12
  • Note that despite your answer accidentally making it look like the opposite is true, your `subprocess.PIPE` examples above DO work with Python < 3.7, such as Python 3.6. – Gabriel Staples Jul 10 '20 at 22:41
  • I've edited your answer and noted that in your answer. – Gabriel Staples Jul 10 '20 at 22:43
  • Huh, I never would have read that as implying they did not work on < 3.7. But, I’m glad you made it that much clearer – SethMMorton Jul 10 '20 at 23:17
  • i;m only getting <_io.BufferedReader name=3> as an output – greendino Oct 18 '20 at 21:45
  • i was runing my flask server in one subprocess in the bg and then starting tests in another subprocess but the outcome was a nightmare! doing subprocess.run(['my', 'process'], stdout=subprocess.DEVNULL) did let my server process work on the background while the second subprocess test run at the same time with out clogging the outputs/prints, and if something goes wrong with the first subprocess(the server) i will still get that print. super thanks for the examples! – pelos Jul 29 '21 at 22:49
  • Note that while the PIPE works **in Python < 3.7, there is no keyword `text`**, so you can only get a bytestring by default and the code above will crash – Epanemu Feb 17 '23 at 14:17
  • @Epanemu That is why the answer has these statements: "`text=True` is Python >= 3.7 only, use `universal_newlines=True` on Python <= 3.6. `universal_newlines=True` is identical to `text=True` but more verbose to type but should exist on all Python versions" – SethMMorton Feb 17 '23 at 17:13
26

ex: to capture the output of ls -a

import subprocess
ls = subprocess.run(['ls', '-a'], capture_output=True, text=True).stdout.strip("\n")
print(ls)
Shaeinst
  • 377
  • 3
  • 5
  • 6
    this works for python 3.7, though not for python 3.6 (I get `TypeError: __init__() got an unexpected keyword argument 'capture_output'`) – BCArg May 20 '21 at 09:36
  • the last part helps get a clean output to a variable. – Lambert Jan 19 '22 at 21:00
  • How does`t work in Flask? – Алексей Jun 28 '22 at 10:06
  • 1
    @Алексей I'm not sure what you mean. This should work fine in Flask, which is still running in a plain Python script and has access to the filesystem, same as any other Python script without Flask. – ggorlen Apr 01 '23 at 02:55