247

How do I retrieve the exit code when using Python's subprocess module and the communicate() method?

Relevant code:

import subprocess as sp
data = sp.Popen(openRTSP + opts.split(), stdout=sp.PIPE).communicate()[0]

Should I be doing this another way?

That1Guy
  • 7,075
  • 4
  • 47
  • 59
CarpeNoctem
  • 5,540
  • 8
  • 30
  • 33

8 Answers8

355

Popen.communicate will set the returncode attribute when it's done(*). Here's the relevant documentation section:

Popen.returncode 
  The child return code, set by poll() and wait() (and indirectly by communicate()). 
  A None value indicates that the process hasn’t terminated yet.

  A negative value -N indicates that the child was terminated by signal N (Unix only).

So you can just do (I didn't test it but it should work):

import subprocess as sp
child = sp.Popen(openRTSP + opts.split(), stdout=sp.PIPE)
streamdata = child.communicate()[0]
rc = child.returncode

(*) This happens because of the way it's implemented: after setting up threads to read the child's streams, it just calls wait.

Eli Bendersky
  • 263,248
  • 89
  • 350
  • 412
  • 67
    This example helped me, but it would be nice if examples didn't do the "import subprocess as sp" pattern of importing something standard as an obscure abbreviation. While this trims 8 characters off the code that follows it, it also makes it difficult to understand and reuse. – uglycoyote Oct 19 '16 at 18:36
  • 27
    @uglycoyote There's no rule that says you have to copy and paste. Just retype it however you want, it's like 4 like lines. – Jason C Oct 31 '16 at 13:45
  • 6
    @uglycoyote you could also edit it to be something like `from subprocess import Popen` and then just use `Popen` instead of `subprocess(or sp).Popen` which I'd say probably increases readability and shortens lines – Mitch Nov 18 '16 at 18:43
  • 4
    Yeah... must call `process.communicate()` and then assign `returncode` to some variable. If the assignment is done before calling `communicate`, is `None`. – WesternGun Dec 29 '17 at 08:39
  • 1
    Is it possible to show the return code without redirecting the pipe? I am calling a bash code and I would like to see the output in real time in the terminal – Nisba Mar 10 '18 at 22:07
  • @Nisba, the redirection is not required. – Ryan McGrath Dec 27 '21 at 16:49
21

.poll() will update the return code.

Try

child = sp.Popen(openRTSP + opts.split(), stdout=sp.PIPE)
returnCode = child.poll()

In addition, after .poll() is called the return code is available in the object as child.returncode.

Matthew Vernon
  • 345
  • 2
  • 5
  • 1
    when I did this .poll() was empty. I had to run child.communicate() in the line above child.poll() for this to work. – NateW Dec 22 '18 at 00:40
  • 3
    I think you meant to use .wait() instead of .poll(), as per documentation: https://docs.python.org/3/library/subprocess.html. Note that .wait() takes an optional timeout param which can be convenient. – gg99 Mar 12 '19 at 11:39
  • [poll](https://docs.python.org/3/library/subprocess.html#subprocess.Popen.poll)() will only update the return code if the child has terminated, otherwise it will return None - [wait](https://docs.python.org/3/library/subprocess.html#subprocess.Popen.wait)() is better here. – Bigminimus Oct 18 '22 at 15:51
19

You should first make sure that the process has completed running and the return code has been read out using the .wait method. This will return the code. If you want access to it later, it's stored as .returncode in the Popen object.

Noufal Ibrahim
  • 71,383
  • 13
  • 135
  • 169
16

Just to point out a common misconception, you should avoid Popen always when you can. To quote the documentation,

The recommended approach to invoking subprocesses is to use the run() function for all use cases it can handle. For more advanced use cases, the underlying Popen interface can be used directly.

If you just want to run a subprocess and wait for it to finish, that's a single line of code with subprocess.run or its legacy siblings subprocess.call and subprocess.check_output, and you don't need to copy/paste and/or understand the intricacies of the communicate and wait etc methods required around the low-level Popen object.

import subprocess

proc = subprocess.run(
    [openRTSP] + opts.split(),
    capture_output=True,
    # avoid having to explicitly encode
    text=True)
data = proc.stdout
result = proc.returncode

If you don't want to capture the output from the process, maybe replace capture_output=True with stdout=subprocess.DEVNULL (and perhaps similarly for stderr); in the absence of either, the output will simply be displayed to the user, outside of Python's control.

Also, unless your options in opts are completely trivial, generally replace the regular string split() here with shlex.split() which understands how to cope with quoted strings.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • This is an adaptation of my answer to [a duplicate question.](https://stackoverflow.com/questions/71232323/is-there-any-way-to-capture-only-the-exit-code-without-the-script-output-in-subp) – tripleee Feb 23 '22 at 08:47
  • 1
    This does not answer the question. – Eric M Sep 21 '22 at 15:30
  • 1
    Sure it does; `proc.returncode` (where `proc` is the `CompletedProcess` object from `subprocess.run`) contains the return code. – tripleee Sep 21 '22 at 16:22
13

Use process.wait() after you call process.communicate().
For example:

import subprocess

process = subprocess.Popen(['ipconfig', '/all'], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
stdout, stderr = process.communicate()
exit_code = process.wait()
print(stdout, stderr, exit_code)
Jossef Harush Kadouri
  • 32,361
  • 10
  • 130
  • 129
8

exitcode = data.wait(). The child process will be blocked If it writes to standard output/error, and/or reads from standard input, and there are no peers.

khachik
  • 28,112
  • 9
  • 59
  • 94
0

This worked for me. It also prints the output returned by the child process

child = subprocess.Popen(serial_script_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    retValRunJobsSerialScript = 0
    for line in child.stdout.readlines():
        child.wait()
        print line           
    retValRunJobsSerialScript= child.returncode
Chinni Mahesh
  • 17
  • 1
  • 4
-2

Please see the comments.

Code:

import subprocess


class MyLibrary(object):

    def execute(self, cmd):
        return subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True,)
      
    def list(self):
        command = ["ping", "google.com"]
        sp = self.execute(command)
        status = sp.wait()  # will wait for sp to finish
        out, err = sp.communicate()
        print(out)
        return status # 0 is success else error


test = MyLibrary()

print(test.list())

Output:

C:\Users\shita\Documents\Tech\Python>python t5.py

Pinging google.com [142.250.64.78] with 32 bytes of data:
Reply from 142.250.64.78: bytes=32 time=108ms TTL=116
Reply from 142.250.64.78: bytes=32 time=224ms TTL=116
Reply from 142.250.64.78: bytes=32 time=84ms TTL=116
Reply from 142.250.64.78: bytes=32 time=139ms TTL=116

Ping statistics for 142.250.64.78:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 84ms, Maximum = 224ms, Average = 138ms

0
Aaj Kaal
  • 1,205
  • 1
  • 9
  • 8