Advertisement
Sometimes in a Python project, you need to reach beyond the language and interact with the system itself—copy a file, ping a server, or run a command-line tool. You don’t have to switch languages or leave your script. Python gives you ways to run shell commands directly, whether it’s a simple one-liner or something more involved.
Some methods have been around for years and still work well; others are newer and offer more control, like capturing output or handling errors cleanly. The right choice depends on how much feedback you need and how the command fits into your code. This article covers nine practical ways to run shell commands in Python, from quick solutions to advanced options.
This one is basic. It’s part of the os module and has been around forever. It runs the command you give it as if you typed it in the terminal. But it doesn’t return the output—just the exit code.
import os
os.system('ls -l')
Use this if you only care whether the command succeeded. It's fine for simple jobs, but it's limited. You can't capture what the command prints unless you redirect it manually to a file.
This came later and offers more control. You pass it the command as a list or a string. It runs the command and returns the exit code. It doesn't capture output either unless you work around it.
import subprocess
subprocess.call(['ls', '-l'])
It’s more secure than os.system() because it avoids shell injection if you pass a list. That matters when building commands from user input.
This is the modern way. It arrived in Python 3.5 and cleaned things up. It can run a command, wait for it to finish, and capture the output if needed. It returns a CompletedProcess object that has details like return code, stdout, and stderr.
import subprocess
result = subprocess.run(['ls', '-l'], capture_output=True, text=True)
print(result.stdout)
This approach is cleaner and easier to debug. You get more feedback, and it’s not much more to write. For many tasks, this is the best balance.
This gives you even more control. It's for advanced cases—when you want to run something in the background, stream the output as it comes in, or handle stdin and stdout while the command runs.
import subprocess
process = subprocess.Popen(['ls', '-l'], stdout=subprocess.PIPE, text=True)
for line in process.stdout:
print(line.strip())
This method is flexible, but a little more complex. You don't always need it, but it's good when you want to process output in real time.
This one is obsolete. It was part of the commands module in Python 2 and used like this:
import commands
output = commands.getoutput('ls -l')
print(output)
You shouldn’t use it in modern code. It's not available in Python 3, and even in Python 2, it had security problems. Avoid unless you’re stuck with legacy code.
Sometimes, your command is in a string, and you want to split it properly—like the shell would—before passing it to the subprocess. That's where shlex helps. It parses the string into a list that avoids quoting errors.
import subprocess
import shlex
command = "ls -l /home/user"
args = shlex.split(command)
subprocess.run(args)
This is safer than just doing .split() on a space. It handles quotes and escapes correctly. It's not a way to run commands on its own, but it's helpful with subprocesses.
This one is older and returns a file-like object for reading the output. It’s less common now, and while it still works, subprocess.run() is usually better.
import os
output = os.popen('ls -l').read()
print(output)
It's simple but less powerful than the subprocess. You don't get an exit code or access to stderr, and it's not great for error handling. Use it for quick scripts or throwaway code.
Some commands ask for input while they run—like passwords or confirmations. That’s where pexpect comes in. It’s not part of the standard library, so you need to install it.
import pexpect
child = pexpect.spawn('ftp ftp.gnu.org')
child.expect('Name .*: ')
child.sendline('anonymous')
child.expect('Password:')
child.sendline('[email protected]')
child.interact()
This module lets your Python code interact with programs like a person would—waiting for prompts and sending responses. It's useful for automation tasks where commands need interaction. A little heavier but necessary in some workflows.
This approach doesn’t run commands directly but lets you write shell scripts from Python and execute them using the shell itself. It’s handy when you want to script multiple steps, pass variables, or maintain portability.
from pathlib import Path
import subprocess
script = Path("temp_script.sh")
script.write_text("""#!/bin/bash
echo "This is a shell script run from Python"
ls -l
""")
script.chmod(0o755)
subprocess.run(["./temp_script.sh"])
You create a shell script from inside Python, make it executable, and run it. This is useful when you want the benefits of shell scripting while staying in the Python workflow. It's not always needed, but it can be a clean solution when you're generating or templating system commands.
Python gives you several ways to run shell commands without stepping outside your code. Whether you need something fast or more detailed, there’s a method that fits. For simple tasks where output doesn’t matter, os.system() works. If you need results or error handling, subprocess.run() is solid and straightforward. For commands needing interaction, pexpect helps automate input and output. These tools let you mix shell logic with Python without confusion. You don’t have to switch contexts or rewrite what already works in bash. Just call it, catch the result, and keep going. Python handles the rest, making it easy to blend scripting with control in a way that stays clean and readable.
Advertisement
How the ORDER BY clause in SQL helps organize query results by sorting data using columns, expressions, and aliases. Improve your SQL sorting techniques with this practical guide
Which data science companies are actually making a difference in 2025? These nine firms are reshaping how businesses use data—making it faster, smarter, and more useful
Sisense adds an embeddable chatbot, enhancing generative AI with smarter, more secure, and accessible analytics for all teams
Thinking about upgrading to ChatGPT Plus? Here's an in-depth look at what the subscription offers, how it compares to the free version, and whether it's worth paying for
Learn about landmark legal cases shaping AI copyright laws around training data and generated content.
How vision language models transform AI with better accuracy, faster processing, and stronger real-world understanding. Learn why these models matter today
Microsoft’s in-house Maia 100 and Cobalt CPU mark a strategic shift in AI and cloud infrastructure. Learn how these custom chips power Azure services with better performance and control
Why is Alibaba focusing on generative AI over quantum computing? From real-world applications to faster returns, here are eight reasons shaping their strategy today
How MobileNetV2, a lightweight convolutional neural network, is re-shaping mobile AI. Learn its features, architecture, and applications in edge com-puting and mobile vision tasks
Explore the top 11 generative AI startups making waves in 2025. From language models and code assistants to 3D tools and brand-safe content, these companies are changing how we create
Discover how Case-Based Reasoning (CBR) helps AI systems solve problems by learning from past cases. A beginner-friendly guide
How the Vertex AI Model Garden supports thousands of open-source models, enabling teams to deploy, fine-tune, and scale open LLMs for real-world use with reliable infrastructure and easy integration