Python Subprocess Module

Subprocess is a built-in module that allows us to spawn new system processes, connect to their input/output/error streams, and obtain their return codes.

import subprocess

Running a System Command

The run function is used to execute a system command.

subprocess.run(['echo', 'Hello World!'])
Hello World!

If you want to run a command as a single string instead of a list, use:

subprocess.run('echo Hello World!', shell=True)
Hello World!

Both will automatically print the output to the terminal.

WARNING

Avoid `shell=True` with untrusted input (risk of shell injection).

Capturing the Output

To capture the output, set capture_output to True:

result = subprocess.run(
  ['echo', 'Hello World!'],
  capture_output=True,
  text=True
)

output = result.stdout
error = result.stderr
returncode = result.returncode
output = "Hello World!\n"
error = ""
returncode = 0

Here, text=True tells the function to read the output as text instead of bytes.

A returncode of 0 indicates success; any non-zero value indicates an error.

Checking for Errors

While we can check for success using the return code, there is a more Pythonic way of doing it:

try:
  result = subprocess.run(
    ['ls', 'my_dir'],
    check=True,
  )
  print('Finished without errors.')
except subprocess.CalledProcessError:
  print('Error: Directory does not exist.')
Error: Directory does not exist.

If check is set to True and the returncode is not 0 (unsuccessful operation), subprocess will raise a subprocess.CalledProcessError, which can be handled in the usual way using an except block.

Running Commands with Timeout

You can set a timeout for a process. If it takes longer than the specified time, subprocess.TimeoutExpired is raised:

try:
    subprocess.run(['sleep', '10'], timeout=5)
except subprocess.TimeoutExpired:
    print('Process took too long!')
Process took too long!

Writing Output to a File

You can redirect the output to a file by setting stdout (and stderr, if needed) to a file object:

with open('output.txt', 'w') as f:
    subprocess.run(['ls', '-l'], stdout=f, stderr=f)

Here, both normal output and errors will appear in the file, in the order they are generated.

Note: text=True is helpful when you want to manipulate the output as a string in Python. It is not strictly required when redirecting output directly to a file.