Bash in Python
Python is an open-source programming language that is interpreted and object-oriented. It is the most popular programming language because it has a large community and a large number of modules in many fields.
In Linux, we need to call a Bash command from Python to run Python in Linux.
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.
Using the subprocess Module
A built-in subprocess module can be used to call the Bash commands in Python.-The-subprocess-module gives us access to spawn processes. It provides many capabilities, such as managing the input and output of a spawned process and getting its-exit status.
Two methods of the-subprocess-module will be used to call Bash commands. The first one subprocess is the-run()-method, and the second subprocess is the-check_output()-method.
Using subprocess.run()
We;ll use the Python script,-subprocess_run.py, to call Bash commands using-subprocess.run():
Code:
import subprocess, sys
command = sys.argv[1:]
subprocess.run(command[0], shell = True, executable="/bin/bash")
The code is broken down and briefly discussed.
First, the subprocess module is imported:
import subprocess
Then the command line is taken, and the script runs from the command line arguments:
command = sys.argv[1:]
The above statement assigns the Bash command passed to the script of the command variable. The Python list given by sys.argv consists of the command-line arguments passed to a Python script. The sys.argv first element, named sys.argv[0], is the name of the script. So, omit and get all of the arguments passed that remain to the script using sys.argv[1:].
For simplicity, we don't handle error cases. So, we don't check whether sys.argv[1]-exists.
Finally, the command is run:
subprocess.run(command[0], shell = True, executable="/bin/bash")
In the above line of code, the first argument, command[0], is the Bash command that is to be run. Subprocess.run() gives a string or list of strings. So, we pass the whole command as a single string using command[0].
Suppose a single string is passed as a command to a subprocess.run(), then the value of the argument must be true for the shell parameter. Other, the command is just passed as a list of strings, and the 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:
Output:
$ ./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 command that is input to the Python script is sudo dnf list | grep -I perf. As can be seen from the output of the script, the command is run successfully.
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.
Using subprocess.check_output()
The python script is used to subprocess_check_output.py, for calling bash command.
Code:
#!/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())
The argument is parsed in the same way as in subprocess_run.py file. The code is broken down after the statement command = sys.argv[1:].
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)
The subprocess.check_output()-is called in a-try-block because if there exit status of the command execution is non-zero, it will 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].
Setting the-stderr-parameter to-subprocess.STDOUT-lets us capture the standard error in the result.
The output of the command execution is stored in the result variable.
The-CalledProcessError-exception is handled in the code snippet below:
except subprocess.CalledProcessError as cpe:
result = cpe.output
In case of a non-zero exit status, the output of the command execution is returned from the output field of the CalledProcessError exception and assigned to the result variable.
Finally, the output of the command execution is printed in the-finally-block:
finally:
for line in result.splitlines():
print(line.decode())
Finally, the block is always run whether the code of exit status is zero or non-zero.-subprocess.check_output()-gives 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.
Ouput:
$ ./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 Copy
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
Using the OS Module:
The built-in OS module in Python is another method for implementing bash commands with the help of Python script. The OS module has many ways to deal with the operating system in a portable form. We'll use the method () in the system() method of the OS module.
The method os.system() executes the command passed in a shell. The value returned by os.system() is the process's exit status in Linux.
Code:
import os, sys
command = sys.argv[1:]
os.system("/bin/bash -c \"" + command[0] + "\"")
Output:
$ ./os_system.py "sudo dnf list | grep -i iperf"
iperf3.x86_64 3.9-11.el9 @appstream
iperf3.i686 3.9-11.el9 appstream