1. Overview

Python is an open-source, interpreted, and object-oriented programming language. It’s one of the most popular programming languages because of its large community base and rich number of modules in diverse fields.

While using Python in Linux, we may need to call Bash commands from Python. In this tutorial, we’ll discuss how to call a Bash command in a Python script.

Firstly, we’ll use the run() and check_output() methods of the built-in subprocess module. Then, we’ll see the system() method of the built-in os module.

2. Using the subprocess Module

We can use the built-in subprocess module of Python to call Bash commands. The subprocess module allows us to spawn processes. It provides many capabilities, such as managing the input and output of a spawned process and getting its exit status.

We’ll use two methods of the subprocess module to call Bash commands. The first one is the run() method, and the second one is the check_output() method.

2.1. Using subprocess.run()

We’ll use the Python script, subprocess_run.py, to call Bash commands using subprocess.run():

#!/usr/bin/python

import subprocess, sys

command = sys.argv[1:]

subprocess.run(command[0], shell = True, executable="/bin/bash")

We’ll break down the code to discuss it briefly.

First, we import the subprocess and sys modules:

import subprocess, sys

Then, we get the command that the script runs from the command line arguments:

command = sys.argv[1:]

The statement above assigns the Bash command passed to the script to the command variable. sys.argv is a Python list that contains the command-line arguments passed to a Python script. The first element of sys.argv, namely sys.argv[0], is the name of the script. So, we omit it and get all of the remaining arguments passed to the script using sys.argv[1:].

We don’t handle error cases for simplicity. So, we don’t check whether sys.argv[1] exists.

Finally, we run the command:

subprocess.run(command[0], shell = True, executable="/bin/bash")

In the code snippet above, *the first argument, command[0], is the Bash command we want to run*. subprocess.run() expects its type to be a string or list of strings. So, we pass the whole command as a single string using command[0].

*If we pass a single string as a command to subprocess.run(), then the value of the argument must be True for the shell parameter*. Otherwise, we must pass the command as a list of strings, and shell must be False.

subprocess.run() uses /bin/sh by default while running the command. We set the shell explicitly to Bash using the last argument executable=”/bin/bash”. This isn’t necessary on distros where /bin/sh is a symbolic link to /bin/bash.

Now, let’s run the script:

$ ./subprocess_run.py "sudo dnf list | grep -i iperf"
iperf3.x86_64                                        3.9-11.el9                            @appstream
iperf3.i686                                          3.9-11.el9                            appstream 

The input command to the Python script is sudo dnf list | grep -i iperf. As is apparent from the output of the script, we’re successful in running the command using subprocess_run.py.

Finally, let’s also try an error case by calling a non-existent command:

$ ./subprocess_run.py "non_existing_command"
/bin/bash: line 1: non_existing_command: command not found

The script is successful in this case, too.

2.2. Using subprocess.check_output()

We’ll use the Python script, subprocess_check_output.py, for calling Bash commands using subprocess.check_output():

#!/usr/bin/python
import subprocess, sys

command = sys.argv[1:]

try:
    result = subprocess.check_output(command, shell = True, executable = "/bin/bash", stderr = subprocess.STDOUT)

except subprocess.CalledProcessError as cpe:
    result = cpe.output

finally:
    for line in result.splitlines():
        print(line.decode())

We parse the arguments in the same way as in subprocess_run.py. Let’s break down the code after the statement command = sys.argv[1:] and analyze it.

In the try block below, we run the command using subprocess.check_output() method:

try:
    result = subprocess.check_output(command, shell = True, executable = "/bin/bash", stderr = subprocess.STDOUT)

We call subprocess.check_output() in a try block because if the exit status of the command execution is non-zero, it raises a CalledProcessError exception.

subprocess.check_output() requires the first parameter, the command to run, to be passed as a Python list. So, we pass command instead of command[0].

The meanings of shell and executable parameters are the same as before.

Setting the stderr parameter to subprocess.STDOUT lets us capture the standard error in the result.

We store the output of the command execution to the result variable.

We handle the CalledProcessError exception in the code snippet below:

except subprocess.CalledProcessError as cpe:
    result = cpe.output

In case of a non-zero exit status, we get the output of the command execution from the output field of the CalledProcessError exception and assign it to the result variable.

Finally, we print the output of the command execution in the finally block:

finally:
    for line in result.splitlines():
        print(line.decode())

The finally block is always run whether the exit status code is zero or non-zero. subprocess.check_output() returns the result as a Python bytes class. The splitlines() method of the bytes class returns a list of the lines in the result variable, breaking at newline characters. We decode bytes to a string object using the line.decode() statement, and finally print the output using the print() function.

Now, let’s run the script:

$ ./subprocess_check_output.py "sudo dnf list | grep -i iperf"
iperf3.x86_64                                        3.9-11.el9                            @appstream
iperf3.i686                                          3.9-11.el9                            appstream 

The output is as expected. Let’s also try an error case:

$ ./subprocess_check_output.py "non_existing_command"
/bin/bash: line 1: non_existing_command: command not found

This output is also as expected. Therefore, we’re successful in calling Bash commands using the check_output() method of the subprocess module.

3. Using the os Module

The built-in os module of Python is another alternative for calling Bash commands from a Python script. The os module has many methods to interact with the operating system in a portable way. The method we’ll use is the system() method of the os module.

The os.system() method executes the command passed to it in a subshell. It calls the standard C library function system() under the hood. The return value of os.system() is the exit status of the process in Linux.

We’ll use the Python script, os_system.py, to call a Bash command:

#!/usr/bin/python
import os, sys

command = sys.argv[1:]
os.system("/bin/bash -c \"" + command[0] + "\"") 

os.system() expects the command that it’ll run as a string. Therefore, after getting the command-line arguments as before, we call os.system(“/bin/bash -c \”” + command[0] + “\””). What we do in this statement is just run the command using /bin/bash. For example, if we want to run the command ls -l, then the command we pass to os.system() is /bin/bash -c “ls -l”.

Let’s run the script:

$ ./os_system.py "sudo dnf list | grep -i iperf"
iperf3.x86_64                                        3.9-11.el9                            @appstream
iperf3.i686                                          3.9-11.el9                            appstream 

We’re successful in running the command sudo dnf list | grep -i iperf as expected.

Let’s try an error case also:

$ ./os_system.py "non_existing_command"
/bin/bash: line 1: non_existing_command: command not found

This output is also as expected.

4. Conclusion

In this article, we discussed how to call a Bash command in a Python script.

First, we learned how to use the run() and check_output() methods of the built-in subprocess module. Then, we saw that using the system() method of the built-in os module is another alternative.