How do I execute a program or call a system command?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
In modern Python, the standard way to execute another program or system command is the subprocess module. The main recommendation is to use subprocess.run for straightforward commands and avoid older shortcuts such as os.system unless you specifically need their simpler but less flexible behavior.
Use subprocess.run for Ordinary Commands
For most cases, subprocess.run is the right starting point.
This runs the command, waits for it to finish, and gives you access to:
- standard output
- standard error
- exit status
Passing the command as a list is safer than passing one shell string because Python does the argument handling directly.
Check for Failures Explicitly
If the command must succeed, use check=True.
With check=True, Python raises CalledProcessError when the command exits with a nonzero status. That is better than silently continuing after a failed external command.
Capture Output Only When You Need It
If you only care that the command runs, you do not need to capture its output.
If you do want the output for later processing, capture_output=True or explicit stdout and stderr pipes are appropriate.
Avoid shell=True Unless You Need Shell Features
Sometimes people write:
That can work, but it brings shell parsing and injection risk. Prefer list-based arguments unless you actually need shell features such as wildcard expansion, pipelines, or shell built-ins.
If user input is involved, avoiding shell=True is especially important.
Use Popen for Long-Running or Interactive Cases
subprocess.run is best for simple request-and-wait execution. If you need streaming I/O, interaction, or more control over the process lifetime, use subprocess.Popen.
That lower-level API is more powerful, but most code should start with run and move to Popen only when needed.
Common Pitfalls
- Using
os.systemwhen structured subprocess control is required. - Passing a shell string with
shell=Truewithout understanding the security implications. - Ignoring the command exit status and treating failed commands as though they succeeded.
- Capturing output unnecessarily and complicating code that only needed to run a command.
- Reaching for
Popenwhensubprocess.runwould have solved the problem more simply.
Summary
- In Python,
subprocess.runis the usual answer for executing external commands. - Pass commands as a list of arguments when possible.
- Use
check=Truewhen failure should stop the workflow. - Avoid
shell=Trueunless you genuinely need shell semantics. - Use
Popenonly for more advanced process-control cases.

