62

How can I call an external program with a python script and retrieve the output and return code?

3

6 Answers 6

89

Look at the subprocess module: a simple example follows...

from subprocess import Popen, PIPE

process = Popen(["ls", "-la", "."], stdout=PIPE)
(output, err) = process.communicate()
exit_code = process.wait()
Sign up to request clarification or add additional context in comments.

5 Comments

I've edited the answer above to reflect Ambroz's suggestion in case someone does not read the comments and uses the previously incorrect code.
if this does not for some reason work, you might want to add shell=True to the params ( when in windows? )
It seems that the above solution can be replaced by simple call subprocess.run() (Python >= 3.5 is required).
can I get the line number of error line like a line has error and the function returns me the line number like 1.0? so that I can add a tag and highlight the error.
In order to grab the error outputs "stderr=PIPE" should also be added: process = Popen(["ls", "-la", "."], stdout=PIPE, stderr=PIPE)
19

Following Ambroz Bizjak's previous comment, here is a solution that worked for me:

import shlex
from subprocess import Popen, PIPE

cmd = "..."
process = Popen(shlex.split(cmd), stdout=PIPE)
process.communicate()
exit_code = process.wait()

2 Comments

This is by far the best answer.
I have a similar post here that shows how to get three things from a process: exitcode, stdout, stderr.
5

After some research, I have the following code which works very well for me. It basically prints both stdout and stderr in real time. Hope it helps someone else who needs it.

stdout_result = 1
stderr_result = 1


def stdout_thread(pipe):
    global stdout_result
    while True:
        out = pipe.stdout.read(1)
        stdout_result = pipe.poll()
        if out == '' and stdout_result is not None:
            break

        if out != '':
            sys.stdout.write(out)
            sys.stdout.flush()


def stderr_thread(pipe):
    global stderr_result
    while True:
        err = pipe.stderr.read(1)
        stderr_result = pipe.poll()
        if err == '' and stderr_result is not None:
            break

        if err != '':
            sys.stdout.write(err)
            sys.stdout.flush()


def exec_command(command, cwd=None):
    if cwd is not None:
        print '[' + ' '.join(command) + '] in ' + cwd
    else:
        print '[' + ' '.join(command) + ']'

    p = subprocess.Popen(
        command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd
    )

    out_thread = threading.Thread(name='stdout_thread', target=stdout_thread, args=(p,))
    err_thread = threading.Thread(name='stderr_thread', target=stderr_thread, args=(p,))

    err_thread.start()
    out_thread.start()

    out_thread.join()
    err_thread.join()

    return stdout_result + stderr_result

1 Comment

I only copy pasted the code to see if it works and I got an error that out is of type bytes so it cannot be used in the write method. Also, it prints the characters, but never stops.
3

Check out the subprocess module here: http://docs.python.org/library/subprocess.html#module-subprocess. It should get what you need done.

Comments

3

I've developed a little library (py-execute) that allows you to execute external programs, retrieve the output and the retcode and, at the same time get output in console in real time:

>>> from py_execute.process_executor import execute
>>> ret = execute('echo "Hello"')
Hello
>>> ret
(0, 'Hello\n')

You can avoid printing to console passing a mock user_io:

>>> from mock import Mock
>>> execute('echo "Hello"', ui=Mock())
(0, 'Hello\n')

I wrote it because with plain Popen (In Python 2.7) I was having trouble executing commands with a long output

Comments

0

I know this is a pretty old thread, but I wanted to add something I found while researching this since I stumbled on this and it helped me quite a bit.

In subprocess, if you don't need total control over PIPE you can use the .run() function.

This is my current code that works rather nicely for my own program.

process = subprocess.run(["./program","cm1","cm2"], text=True)
print(process)

And this outputs the stdout live. This can also be configured to output both stdout and stderr in the same stream.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.