HOWTO · Python
How to Call External Program in Python
This tutorial demonstrates how to run external programs in Python
On this page
Use subprocess.run() for most ordinary external commands. Pass the executable and its arguments as a list so Python does not ask a shell to parse them. Set capture_output=True and text=True when you need readable output, then inspect stdout, stderr, and returncode on the completed-process object. The example below starts Python itself, so it does not depend on a shell utility being installed.
Run this example:
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-c", "print('Hello from Python')"],
capture_output=True,
text=True,
check=True,
)
print(result.stdout, end="")
print("Return code:", result.returncode)
Hello from Python
Return code: 0
A list keeps each argument separate, including spaces inside an argument. text=True decodes captured streams to strings using the platform’s text encoding; without it, stdout and stderr are bytes. check=True makes a nonzero command status raise subprocess.CalledProcessError. Leave it out when you want to handle the status yourself. Captured output is separate from the command’s status: a command can write to either stream and still finish with a nonzero returncode.
Use os.system() to run a shell command
os.system() runs its string through a command processor (a shell on common platforms) and returns the command processor’s status. The command’s standard output and standard error normally remain connected to the parent process, so this call does not give you captured text. It is a legacy choice for a simple shell command when the shell behavior is intentional. For an argument list, captured streams, or structured status handling, prefer subprocess.run().
Because the input is shell syntax, do not build the string by inserting untrusted text. Shell metacharacters can change what runs. The exact command processor and status encoding vary by platform, so avoid assuming that a returned value is always the child program’s unmodified exit code. For example, the portable conceptual call is os.system("some-command"); choose a concrete command and quoting rules for the operating system where it will run.
Use os.popen() to read command output
os.popen() opens a pipe to a command run through a shell and returns a file-like object. Read the stream and close it when finished; close() can also provide the command status. This interface is a legacy option for simple pipe-based interaction, and it does not offer the separate stdout and stderr handling available through subprocess.
For example, the shape is pipe = os.popen(command), followed by output = pipe.read() and status = pipe.close(). The command string is interpreted by a shell, so apply the same care to untrusted input as with os.system(). The return value from close() is not a replacement for captured output, and its status representation can depend on the platform. Prefer subprocess.run() for new code that needs both output and a clear return code.
Use subprocess.Popen() for lower-level process control
subprocess.Popen() is the class constructor used when a program needs to interact with a process while it is running. Pass an argument list for direct execution. Set stdout=subprocess.PIPE or stderr=subprocess.PIPE only when you plan to read those streams. Call communicate() to collect configured pipes and wait for completion, or call wait() when no piped output needs to be collected.
Reading one pipe manually while the child writes heavily to another can fill an operating-system buffer and stall the processes. communicate() is the usual way to collect both streams together; it returns bytes unless text mode is enabled. For large or streaming output, process the streams deliberately rather than buffering everything in memory. If you only need to wait and inspect a completed result, subprocess.run() is simpler.
Use subprocess.call() to run a command and wait
subprocess.call() runs a command, waits for it to finish, and returns its status. Provide a list such as subprocess.call(["program", "--version"]) to avoid shell parsing. This convenience function does not capture output by default; the child’s streams are inherited. Use subprocess.run() when you need captured streams, text decoding, or the CompletedProcess fields. call() remains useful when existing code needs only a synchronous status, but new code generally benefits from the clearer run() interface.
A zero status commonly indicates success, while a nonzero status signals that the command reported a problem. The meaning of particular nonzero values belongs to the invoked program, and status conventions can vary across platforms. If you need to treat a nonzero status as an error, subprocess.run(..., check=True) raises CalledProcessError; otherwise inspect returncode and decide what your application should do.
Use subprocess.run() for most external commands
For ordinary tasks, subprocess.run() waits for completion and returns a CompletedProcess. With capture_output=True, the result has stdout and stderr; with text=True, those values are strings. The example prints standard output and the return code, while standard error remains available in result.stderr. If the program writes output to standard error, it will not appear in result.stdout.
Prefer argument lists and keep shell=False, the default, for direct program invocation. This prevents shell metacharacters in an argument from being interpreted as shell syntax, though the target program still has its own input and option-handling risks. Use shell=True only when shell features such as pipelines or redirection are required, and then keep the shell command fixed or construct it with careful platform-appropriate quoting. An argument list is the portable default, but executable names and command-line options still differ between operating systems.
When a command fails, choose how to handle that result: use check=True to stop with CalledProcessError, or leave it false and branch on result.returncode. A missing executable or inability to start the process raises an operating-system error before a completed result exists. Do not infer success from output text alone; use the status and, when relevant, examine both streams.
Conclusion
Use subprocess.run() for most external commands: it keeps arguments separate, waits for the process, and gives you a result containing status and any captured streams. Use subprocess.Popen() when you need lower-level interaction while a process runs. subprocess.call() is a small synchronous status-only convenience. os.system() and os.popen() are shell-based legacy interfaces; choose them only when their behavior fits the task. Keep shell input trusted, capture only the streams you need, and account for differences in executable names and status conventions across platforms.