47

I want to check if a subprocess has finished execution successfully or failed. Currently I have come up with a solution but I am not sure if it is correct and reliable. Is it guaranteed that every process outputs its errors only to stderr respectfully to stdout:

Note: I am not interested in just redirecting/printing out the output. That I know already how to do.

pipe = subprocess.Popen(command,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                universal_newlines=True)

if "" == pipe.stdout.readline():
    print("Success")
    self.isCommandExectutionSuccessful = True

if not "" == pipe.stderr.readline():
    print("Error")
    self.isCommandExectutionSuccessful = True

alternatively:

   if "" == pipe.stdout.readline():
       print("Success")
       self.isCommandExectutionSuccessful = True
   else:
       print("Error")
       self.isCommandExectutionSuccessful = False

and:

   if not "" == pipe.stderr.readline():
       print("Success")
       self.isCommandExectutionSuccessful = True
   else:
       print("Error")
       self.isCommandExectutionSuccessful = False
martineau
  • 112,593
  • 23
  • 157
  • 280
Zingam
  • 4,230
  • 6
  • 25
  • 44

6 Answers6

42

Do you need to do anything with the output of the process?

The check_call method might be useful here. See the python docs here: https://docs.python.org/2/library/subprocess.html#subprocess.check_call

You can then use this as follows:

try:
  subprocess.check_call(command)
except subprocess.CalledProcessError:
  # There was an error - command exited with non-zero code

However, this relies on command returning an exit code of 0 for succesful completion and a non-zero value for an error.

If you need to capture the output as well, then the check_output method may be more appropriate. It is still possible to redirect the standard error if you need this as well.

try:
  proc = subprocess.check_output(command, stderr=subprocess.STDOUT)
  # do something with output
except subprocess.CalledProcessError:
  # There was an error - command exited with non-zero code

See the docs here: https://docs.python.org/2/library/subprocess.html#subprocess.check_output

Community
  • 1
  • 1
elParaguayo
  • 1,208
  • 1
  • 13
  • 23
  • Well I actually output stdout and stderr it to a window and then I need to check if there is an error and set a boolean variable that is used in another part of the program. I tried returncode but it is None regardless of success or error. – Zingam Aug 01 '14 at 11:40
  • 2
    OK. There's also the `check_output` method. You can get the stdout and stderr from those and still catch the error. See here: https://docs.python.org/2/library/subprocess.html#subprocess.check_output – elParaguayo Aug 01 '14 at 11:42
20

Complete solution with check on return code, stdout and stderr:

import subprocess as sp

# ok
pipe = sp.Popen( 'ls /bin', shell=True, stdout=sp.PIPE, stderr=sp.PIPE )
# res = tuple (stdout, stderr)
res = pipe.communicate()
print("retcode =", pipe.returncode)
print("res =", res)
print("stderr =", res[1])
for line in res[0].decode(encoding='utf-8').split('\n'):
  print(line)

# with error
pipe = sp.Popen( 'ls /bing', shell=True, stdout=sp.PIPE, stderr=sp.PIPE )
res = pipe.communicate()
print("retcode =", pipe.returncode)
print("res =", res)
print("stderr =", res[1])

Prints:

retcode = 0
res = (b'bash\nbunzip2\nbusybox\nbzcat\n...zmore\nznew\n', b'')
stderr = b''
bash
bunzip2
busybox
bzcat
...
zmore
znew

retcode = 2
res = (b'', b"ls: cannot access '/bing': No such file or directory\n")
stderr = b"ls: cannot access '/bing': No such file or directory\n"
PJ_Finnegan
  • 1,721
  • 1
  • 18
  • 16
4
      output,error=pipe.communicate()

This will wait for command to finish and give you output or error depending on the state of command.

vks
  • 65,133
  • 10
  • 87
  • 119
3

You can check return code of the process using check_call() method. In case if process returned non-zero value CalledProcessError will be raised.

  • 2
    @Zingam: `p.returncode is None` means that the process is still running. Note: `p.returncode` can't be `None` after `p.communicate()`. – jfs Aug 01 '14 at 12:10
0

This is how I did it finally:

    # Call a system process
    try:
        # universal_newlines - makes manual decoding of subprocess.stdout unnecessary
        output = subprocess.check_output(command,
                                         stderr=subprocess.STDOUT,
                                         universal_newlines=True)

        # Print out command's standard output (elegant)
        self.textEdit_CommandLineOutput.insertPlainText(output)
        self.isCommandExecutionSuccessful = True

    except subprocess.CalledProcessError as error:
        self.isCommandExecutionSuccessful = False

        errorMessage = ">>> Error while executing:\n"\
                       + command\
                       + "\n>>> Returned with error:\n"\
                       + str(error.output)
        self.textEdit_CommandLineOutput.append(errorMessage)

        QMessageBox.critical(None,
                             "ERROR",
                             errorMessage)
        print("Error: " + errorMessage)

    except FileNotFoundError as error:
        errorMessage = error.strerror
        QMessageBox.critical(None,
                             "ERROR",
                             errorMessage)
        print("Error: ", errorMessage)

I hope it will be useful to someone else.

Zingam
  • 4,230
  • 6
  • 25
  • 44
  • it is incorrect. `check_output()` returns all output as a single string. `for character in some_string` yields characters in Python, not lines. Also you your GUI will be frozen while the child process is running. You should use threads or async. io to avoid blocking the GUI. See [code examples in the links](http://stackoverflow.com/q/15362372/4279) – jfs Jun 22 '15 at 16:29
0

All previous answers are correct but here is another way to skin a cat

(this is test with Python 3.9)

cmd_aws="aws s3 cp s3://some_bucket/somefile+" "+"somefile_name"
            proc_prep = subprocess.Popen(cmd_aws, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            out, err = proc_prep.communicate()
            if err:
                print(out)
                print(err)
grepit
  • 18,790
  • 6
  • 95
  • 76