How to run a program from Python: how to run a system command or another Python script (complete guide)
September 2, 2024
Table of Contents
1. The subprocess module in Python
2. How to pass arguments to a program in subprocess.run
3. Getting the result of command execution (standard output, stdout))
4. Working with the standard error (stderr)
5. Automatic command splitting into a list of arguments
6. How to run a command with wildcards
9. How to run another Python script from a Python script
10. How to terminate a running application by timeout
11. subprocess.Popen is an advanced version of subprocess.run
12. Errors when using subprocess.run
12.1 NameError: name 'subprocess' is not defined. Did you forget to import 'subprocess'?
12.2 FileNotFoundError: [Errno 2] No such file or directory
12.3 subprocess.TimeoutExpired: Command … timed out
The subprocess module in Python
The subprocess module allows you to create new processes. With subprocess, you can, for example, execute any Linux or Windows commands from a script. You can also run another Python script using Python.
With subprocess, you can:
- run an operating system program or another Python script
- pass any number of arguments to the program being run
- connect to standard input/output/error streams and get the return code
- get the output of the running command
- check that the command was executed without errors
The subprocess module is included in the standard Python packages and does not require installation.
The subprocess.run function is the main way to work with the subprocess module.
The simplest way to use the function is to run it like this:
import subprocess result = subprocess.run('ls')
The result of the command is output to standard output – this can be changed, below is how to do it.
The result variable now contains a special CompletedProcess object. From this object, you can get information about the process execution, such as the return code:
print(result.returncode)
Code 0 means that the program was executed successfully.
How to pass arguments to a program in subprocess.run
If you try to specify any arguments to the program being launched, you will encounter an error. For example, the following is an INCORRECT way of passing arguments:
import subprocess result = subprocess.run('ls -ls')
When executing this script, the error “FileNotFoundError: [Errno 2] No such file or directory” will occur.
If you need to call a command with arguments, you need to pass it this way (as a list):
import subprocess result = subprocess.run(['ls', '-ls'])
Getting the result of command execution (standard output, stdout))
By default, the run function returns the result of command execution to the standard output.
If you want to change this default behavior, namely, save the command output to a variable instead of immediately displaying it on the screen, you need to add the stdout argument and set it to subprocess.PIPE.
Since in this case a byte stream will be returned rather than a string, you need to decode it. This can be done with a separate command, but it is more convenient to specify the encoding='utf-8' option to automatically decode the bytes into a string:
import subprocess result = subprocess.run(['ls', '-ls'], stdout=subprocess.PIPE, encoding='utf-8') print(result.stdout)
If you did not specify the encoding='utf-8' option, then to decode the received bytes, you need to run decode:
import subprocess result = subprocess.run(['ls', '-ls'], stdout=subprocess.PIPE) print(result.stdout.decode('utf-8'))
Working with the standard error (stderr)
If the command was executed with an error or did not work correctly, the command output will go to the standard error stream.
You can get this output in the same way as the standard output stream:
import subprocess result = subprocess.run(['ping', '-c', '3', '-n', 'a'], stderr=subprocess.PIPE, encoding='utf-8') print(result.stdout) print(result.stderr) print(result.returncode)
The following will be output:
None ping: a: Name or service not known 2
Result.stdout (what the program should output) now contains only an empty string (None); result.stderr contains the standard error output stream; and result.returncode contains the error code
Automatic command splitting into a list of arguments
I think manually splitting a ready-made command line into arguments is not the most interesting activity. Besides, if you are used to the convenient command launch in other programming languages, when you can specify the command and its arguments in one line, then manually splitting the command into arguments for subprocess.run seems like unnecessary work. Moreover, there are some tricky cases where it is not always obvious how to split the command line into arguments, for example:
/bin/vikings -input eggs.txt -output "spam spam.txt" -cmd "echo '$MONEY'"
You can use automatic splitting of the command with arguments into a list that needs to be passed to subprocess.run. Example:
import shlex, subprocess command_line = 'ls -ls' args = shlex.split(command_line) result = subprocess.run(args)
In this script, the full command, along with arguments, is stored in the variable command_line. Then shlex.split splits the command into arguments and stores the resulting list in the variable args. This list is then passed to subprocess.run to run the command.
How to run a command with wildcards
When trying to run a command using wildcard expressions, such as *:
import subprocess result = subprocess.run(['ls', '-ls', '*php'])
An error will occur:
ls: cannot access '*php': No such file or directory
To call commands that use wildcard expressions, you need to add the shell=True argument and call the command like this:
import subprocess result = subprocess.run('ls -ls *php', shell=True)
Calling a command without breaking it into a list of arguments. Running a command in the shell. The shell=True option
You may have noticed that the previous command did not need to be broken into arguments – it was passed entirely to subprocess.run. This was made possible by the shell=True option, which means that the specified command should be executed in the shell.
That is, you can use the shell=True option and you no longer have to split the command into a list of arguments:
import subprocess result = subprocess.run('ls -ls', shell=True)
See also: https://docs.python.org/3/library/subprocess.html#security-considerations
How to make subprocess.run not wait for the command to complete. Putting a running process into the background
Another feature of the run() function is that it waits for the command to complete. If you try, for example, to run the ping command, this aspect will be noticeable:
import subprocess result = subprocess.run(['ping', '-c', '3', '-n', '8.8.8.8'])
You can put a running process into the background. To do this, you need to:
1) use the shell=True argument
2) write the command line without breaking it into arguments
3) add the “&” symbol at the end of the command after a space
For example:
import subprocess result = subprocess.run('ping -c 3 -n 8.8.8.8 &', shell=True) print('It has been run!')
In this case, the message "It has been run!" will be displayed before the ping command is run.
How to run another Python script from a Python script
In general, running a Python program from another Python program is similar to the process of running an OS program.
Example:
import subprocess result = subprocess.run(['python', 't37.py'])
In this case, the Python script located in the t37.py file will be launched — this file is located in the same directory as the program that launches it, otherwise you need to specify the full path, for example:
import subprocess result = subprocess.run(['python', '/home/mial/test/p/t37.py'])
And another example with the shell=True option:
import subprocess result = subprocess.run('python t37.py', shell=True)
If you have met the conditions that allow you to run a script directly in the shell without explicitly specifying the interpreter (for this, the file must be executable, and the shebang must be specified at the beginning of the file), then you can launch a Python script from another Python program as follows:
import subprocess result = subprocess.run('/home/mial/test/p/t37.py')
And another option with the shell=True option:
import subprocess result = subprocess.run('/home/mial/test/p/t37.py', shell=True)
How to terminate a running application by timeout
Using the timeout option, you can specify the execution time of the running program, after which the application will be terminated forcibly and an error will be displayed. The time is specified in seconds. Example:
import subprocess result = subprocess.run('ping -c 3 -n 8.8.8.8', shell=True, timeout=1)
You can trap error messages with the try…except construct:
import subprocess try: result = subprocess.run('ping -c 1 -n 8.8.8.7', shell=True, timeout=1) except (subprocess.TimeoutExpired): print("Time is over")
subprocess.Popen is an advanced version of subprocess.run
You can also use subprocess.Popen, where the creation and management of the underlying process in this module is handled by the Popen class. It provides more flexibility so that developers can handle less common cases not covered by the subprocess.run functions.
For example, with subprocess.Popen you can run programs as a different user.
See also: https://docs.python.org/3/library/subprocess.html#subprocess.Popen
Errors when using subprocess.run
NameError: name 'subprocess' is not defined. Did you forget to import 'subprocess'?
Full error text:
Traceback (most recent call last): File "/home/mial/test/p/t38.py", line 2, in <module> result = subprocess.run('ls -ls') ^^^^^^^^^^ NameError: name 'subprocess' is not defined. Did you forget to import 'subprocess'?
This error means that you have not imported the subprocess module. That is, at the beginning of your script you need to add the line:
import subprocess
FileNotFoundError: [Errno 2] No such file or directory
Full error text:
Traceback (most recent call last): File "/home/mial/test/p/t38.py", line 2, in <module> result = subprocess.run('ls -ls') ^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/subprocess.py", line 548, in run with Popen(*popenargs, **kwargs) as process: ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/subprocess.py", line 1026, in __init__ self._execute_child(args, executable, preexec_fn, close_fds, File "/usr/lib/python3.12/subprocess.py", line 1955, in _execute_child raise child_exception_type(errno_num, err_msg, err_filename) FileNotFoundError: [Errno 2] No such file or directory: 'ls -ls'
This error can occur if you pass a single command line instead of a list of arguments. As an alternative to the list of arguments, you can use the "shell=True" option.
subprocess.TimeoutExpired: Command … timed out
Full error text:
Traceback (most recent call last): File "/home/mial/test/p/t38.py", line 2, in <module> result = subprocess.run('ping -c 3 -n 8.8.8.8', shell=True, timeout=1) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/subprocess.py", line 550, in run stdout, stderr = process.communicate(input, timeout=timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/subprocess.py", line 1209, in communicate stdout, stderr = self._communicate(input, endtime, timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/subprocess.py", line 2141, in _communicate self.wait(timeout=self._remaining_time(endtime)) File "/usr/lib/python3.12/subprocess.py", line 1264, in wait return self._wait(timeout=timeout) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/lib/python3.12/subprocess.py", line 2045, in _wait raise TimeoutExpired(self.args, timeout) subprocess.TimeoutExpired: Command 'ping -c 3 -n 8.8.8.8' timed out after 0.9999772470036987 seconds
This error occurs when the timeout expires, that is, the time allocated for the execution of the program being launched. If you need to use a timeout, but you do not want your script to terminate due to this error, use the try…except construct.
Related articles:
- How to show all errors in PHP 8 (85.5%)
- Error “Authentication helper program /usr/lib64/squid/basic_ncsa_auth: (2) No such file or directory” (SOLVED) (50.6%)
- UEFI does not see installed Linux (SOLVED) (50.6%)
- What happens if an IPv4 client tries to access an IPv6-only server (SOLVED) (50.6%)
- “Failed - Network error” when exporting from phpMyAdmin (SOLVED) (50.6%)
- How to find out which shell is in use in Linux (RANDOM - 22.7%)