Question

How to Execute System Commands in Python

pythonshellterminalsubprocesscommand

Question

How can I run an external program or system command from Python, similar to typing the command directly into a shell or command prompt?

For example, I want to launch a command-line program from Python code, optionally pass arguments to it, and possibly capture its output or check whether it succeeded.

Short Answer

By the end of this page, you will understand how Python runs external commands, when to use the subprocess module, how to pass arguments safely, how to capture output, and how to avoid common mistakes such as shell injection or incorrect argument formatting.

Concept

Running a system command from Python means starting another program from your Python script.

In Python, the standard and recommended way to do this is with the subprocess module. It lets your code:

  • start another program
  • pass command-line arguments
  • wait for it to finish
  • read its output
  • check its exit status

This matters because real programs often need to interact with other tools. For example:

  • a Python script may call git
  • a deployment script may run docker or npm
  • a data pipeline may execute a compression tool
  • a utility script may open another application

The key idea is that Python is not "typing into a terminal" the way a human does. Instead, it asks the operating system to start another process.

The modern recommendation is:

  • use subprocess.run() for most simple cases
  • use subprocess.Popen() for more control
  • avoid older approaches like os.system() unless you specifically need its limited behavior

One of the most important parts of this topic is safety. If you build command strings from user input and pass them through a shell, you can accidentally create serious security problems. That is why Python usually works best when you pass the command and its arguments as a list.

Mental Model

Think of your Python script as a manager in an office.

  • Python is the manager.
  • An external command is another worker.
  • subprocess.run() is the manager giving that worker a task.
  • The argument list is the instruction sheet.
  • The return code is the worker reporting success or failure.
  • Captured output is the written report the worker brings back.

If you send the instructions clearly as separate pieces, the task is usually done correctly. If you send one big messy string through a shell, the shell has to interpret it first, which adds complexity and risk.

Take Quiz

Syntax and Examples

Basic syntax

The most common way to run a command is:

import subprocess

subprocess.run(["command", "arg1", "arg2"])

This starts the program and waits for it to finish.

Example: run a simple command

import subprocess

subprocess.run(["echo", "Hello from Python"])

This runs the echo command with one argument.

Capture output

import subprocess

result = subprocess.run(
    ["echo", "Hello"],
    capture_output=True,
    text=True
)

print(result.stdout)
print(result.returncode)

What this does

  • capture_output=True collects standard output and standard error
  • text=True returns strings instead of bytes
  • result.stdout contains normal output
  • result.stderr contains error output
  • result.returncode is usually 0 on success

Check for failure automatically

import subprocess

subprocess.run(["python", "--version"], check=True)

With check=True, Python raises an exception if the command exits with a non-zero status.

Passing a string to a shell

import subprocess

subprocess.run("dir", shell=True)

This can work, especially for shell built-ins, but should be used carefully.

In most cases, prefer this instead:

import subprocess

subprocess.run(["ls", "-l"])

or on Windows:

import subprocess

subprocess.run(["cmd", "/c", "dir"])

Example with user input done safely

import subprocess

filename = "report.txt"
result = subprocess.run(["cat", filename], capture_output=True, text=True)
print(result.stdout)

The filename is passed as a separate argument, so it is not interpreted as shell syntax.

Step by Step Execution

Consider this example:

import subprocess

result = subprocess.run(
    ["echo", "Python subprocess"],
    capture_output=True,
    text=True
)

print("Return code:", result.returncode)
print("Output:", result.stdout)

Here is what happens step by step:

  1. Python imports the subprocess module.
  2. subprocess.run() is called.
  3. Python asks the operating system to start the echo program.
  4. The program receives one argument: Python subprocess.
  5. Because capture_output=True, the program's output is saved instead of printed directly to the terminal.
  6. Because text=True, the saved output is decoded into a normal Python string.
  7. The command finishes.
  8. Python stores the result in result.
  9. result.returncode is printed. For a successful command, this is usually 0.
  10. result.stdout is printed, which contains the command output.

A typical output might be:

Return code: 0
Output: Python subprocess

If the command failed and you had used check=True, Python would raise a subprocess.CalledProcessError.

Real World Use Cases

Running external commands is common in practical Python work.

Automation scripts

A script may:

  • create folders
  • copy files
  • compress logs
  • run backup tools

Development tools

Python can call tools such as:

  • git to inspect repositories
  • pytest to run tests
  • npm to build frontend assets
  • docker to start containers

Data processing

A Python program may use existing command-line tools for:

  • image conversion
  • video encoding
  • file decompression
  • database export/import

System administration

Admins often use Python to:

  • restart services
  • inspect processes
  • query system information
  • schedule maintenance tasks

Integration with legacy tools

Sometimes a company already has reliable command-line programs. Python can act as the glue code that calls them and processes their results.

Take Quiz

Real Codebase Usage

In real projects, developers usually wrap command execution in small helper functions instead of scattering subprocess.run() everywhere.

Common patterns

Guard clauses

Check that needed values exist before running the command.

import subprocess


def show_file(path):
    if not path:
        raise ValueError("path is required")

    return subprocess.run(["cat", path], capture_output=True, text=True, check=True)

Early failure with check=True

This makes scripts fail immediately when an important command does not succeed.

subprocess.run(["git", "status"], check=True)

Validation before execution

If arguments come from configuration or users, validate them first.

allowed_envs = {"dev", "staging", "prod"}
env = "dev"

if env not in allowed_envs:
    raise ValueError("Invalid environment")

Capturing output for later logic

Programs often run a command, then use its output inside Python.

result = subprocess.run(["python", "--version"], capture_output=True, text=True)
version_text = result.stdout or result.stderr

Error handling

import subprocess

try:
    subprocess.run(["git", "pull"], check=True)
except subprocess.CalledProcessError as error:
    print("Command failed:", error)

Working directory and environment

Projects often run commands in a specific folder or with custom environment variables.

import os
import subprocess

env = os.environ.copy()
env["MY_SETTING"] = "1"

subprocess.run(["python", "build.py"], cwd="/path/to/project", env=env, check=True)

This is cleaner and more predictable than changing global process state unnecessarily.

Common Mistakes

1. Using one command string when a list is safer

Broken approach:

import subprocess

filename = "my file.txt"
subprocess.run("cat " + filename, shell=True)

Problems:

  • spaces can break the command
  • shell metacharacters may be interpreted
  • user input can become dangerous

Better approach:

import subprocess

filename = "my file.txt"
subprocess.run(["cat", filename])

2. Forgetting that shell built-ins are different

Some commands are not standalone programs. For example, dir on Windows is usually handled by cmd.

import subprocess

subprocess.run(["dir"])

This may fail because dir is not an executable program.

Use:

subprocess.run(["cmd", "/c", "dir"])

3. Not capturing output when you need it

import subprocess

result = subprocess.run(["echo", "hello"])
print(result.stdout)

result.stdout will usually be None here because output was not captured.

Correct:

result = subprocess.run(["echo", "hello"], capture_output=True, text=True)
print(result.stdout)

4. Forgetting text=True

Without text=True, output is returned as bytes.

result = subprocess.run(["echo", "hello"], capture_output=True)
print(result.stdout)

This may print something like:

b'hello\n'

Use:

result = subprocess.run(["echo", "hello"], capture_output=True, text=True)

5. Ignoring failure codes

If you do not check the result, your script may continue after a failed command.

subprocess.run(["some_command"])
print("Script continues even if command failed")

Safer:

subprocess.run(["some_command"], check=True)

6. Using shell=True unnecessarily

shell=True is sometimes needed, but many beginners overuse it.

Avoid it unless:

  • you specifically need shell syntax like pipes or wildcards
  • you are intentionally using a shell built-in
  • you fully understand the security implications

7. Assuming commands are portable

A command that works on Linux may not exist on Windows, and vice versa.

Examples:

  • ls vs dir
  • cat vs type
  • shell quoting rules differ by platform

Always think about the operating system where your script will run.

Comparisons

ApproachRecommended?Best forNotes
subprocess.run([...])YesMost command execution tasksSafe and simple when using a list of arguments
subprocess.run("...", shell=True)SometimesShell features like pipes, redirects, built-insMore flexible, but riskier
subprocess.Popen(...)Yes, when neededAdvanced process controlUseful for streaming, background processes, and manual I/O
os.system("...")Usually noVery simple legacy codeLimited control and harder to work with safely

subprocess.run() vs os.system()

  • subprocess.run() gives you output, errors, return codes, environment control, and argument safety.
  • os.system() mostly just runs a command string.

List arguments vs shell string

StyleExampleSafer?Why
Listsubprocess.run(["grep", "word", "file.txt"])YesArguments are passed directly
String with shellsubprocess.run("grep word file.txt", shell=True)Less safeShell parses the string

run() vs Popen()

  • Use run() when you want to start a command and wait for it.
  • Use Popen() when you need more control, such as:
    • reading output gradually
    • sending input manually
    • running the process in the background
Take Quiz

Cheat Sheet

import subprocess

Run a command

subprocess.run(["command", "arg1", "arg2"])

Capture output as text

result = subprocess.run(
    ["command", "arg1"],
    capture_output=True,
    text=True
)

print(result.stdout)
print(result.stderr)
print(result.returncode)

Raise an error if the command fails

subprocess.run(["command"], check=True)

Run in a specific directory

subprocess.run(["command"], cwd="/path/to/folder")

Use custom environment variables

import os

env = os.environ.copy()
env["DEBUG"] = "1"
subprocess.run(["command"], env=env)

When to use shell=True

Use only when you need shell behavior such as:

  • pipes: a | b
  • redirection: >
  • shell built-ins

Example:

subprocess.run("echo hello > out.txt", shell=True)

Important rules

  • Prefer a list of arguments over a single string.
  • Prefer subprocess.run() for normal use.
  • Use check=True when failure should stop the program.
  • Use capture_output=True, text=True when you need readable output.
  • Be careful with cross-platform command differences.
  • Avoid passing untrusted input through shell=True.

FAQ

How do I run a terminal command in Python?

Use subprocess.run():

import subprocess
subprocess.run(["echo", "hello"])

What is the best way to execute a shell command in Python?

Usually, subprocess.run() with a list of arguments is the best choice because it is safer and easier to control.

How do I capture command output in Python?

Use capture_output=True and text=True:

result = subprocess.run(["echo", "hello"], capture_output=True, text=True)
print(result.stdout)

What does shell=True do in Python?

It runs the command through the system shell. This allows shell features, but it can introduce security and quoting problems.

How do I know whether a command succeeded?

Check result.returncode, or use check=True to raise an exception automatically on failure.

Why does my command work in the terminal but not in Python?

Possible reasons include:

  • the command is a shell built-in
  • the executable is not in PATH
  • quoting rules are different
  • the working directory is different

Should I use os.system() or subprocess?

Prefer subprocess. It is the modern and more powerful solution.

How do I pass arguments with spaces safely?

Pass them as separate list items:

subprocess.run(["cat", "my file.txt"])
Take Quiz

Mini Project

Description

Build a small Python utility that runs a system command entered in code, captures the output, and reports whether the command succeeded. This demonstrates the most common real-world use of subprocess.run(): executing a command safely and reading the result inside Python.

Goal

Create a Python script that runs an external command, prints its output, prints any error output, and shows the exit code.

Requirements

  • Use Python's subprocess module
  • Run a command by passing the program and arguments as a list
  • Capture both standard output and standard error
  • Print the exit code after the command finishes
  • Handle command failure without crashing unexpectedly
Take Quiz

Keep learning

Related questions

View more