-
Section 1: Getting Started
-
Section 2: Core Syntax and Data Types
-
Section 3: Collections
-
39: Set Operations: Union, Intersection, Difference
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Turtle Graphics and Early Practice Projects
-
Section 7: Working with Files and I/O
-
Section 8: Regular Expressions
-
Section 9: Object-Oriented Python
-
Section 10: Error Handling
-
Section 11: Modules and Packages
-
Section 12: Iterators, Generators, and Functional Tools
-
Section 13: Decorators and Metaprogramming
-
Section 14: Concurrency and Parallelism
-
Section 15: Working with Dates, Times, and Numbers
-
Section 16: Standard Library Deep Dive I: Data Structures
-
Section 17: Standard Library Deep Dive II: System and Introspection
-
Section 18: Standard Library Deep Dive III: Security and Encoding
-
Section 19: Standard Library Deep Dive IV: Text and Data Utilities
-
Section 20: Networking and Web Basics
-
Section 21: Working with Databases
-
Section 22: Testing and Quality
-
Section 23: Advanced Typing
-
Section 24: Context Managers and Resource Handling
-
Section 25: Text, Unicode, and Binary Data
-
Section 26: More Functional and Iteration Tools
-
Section 27: Data Validation and Configuration
-
Section 28: Working with Images and Media
-
Section 29: Property-Based and Documentation Testing
-
Section 30: Packaging and Deployment
-
Section 31: Performance and Internals
-
Section 32: Design Patterns in Python
-
Section 33: GUI Programming
-
Section 34: Security Basics
-
Section 35: Data Structures and Algorithms
-
Section 36: Practical Projects
-
Section 37: Capstone Projects
-
Section 38: Interview and Algorithm Practice
-
Section 39: Writing Idiomatic Python
243: The subprocess Module for Running External Commands
Imagine you're a project manager. You're great at organizing the big picture, but you aren't a plumber. When a pipe bursts in the office, you don't try to fix it yourself using a manual you found online; you call a licensed plumber. You give them the address, tell them what's leaking, and then you wait for them to finish and tell you, "All set, it's fixed," or "I couldn't fix it because the main valve is rusted shut."
In this scenario, you are the Python script. The plumber is an external program—something already installed on your operating system, like git, ping, or ls. Python is powerful, but it's often inefficient to rewrite a tool that the OS already provides. The subprocess module is how you "hire" those external tools to do a specific job and report back to you.
Letting Python Step Out of the Bubble
For a long time, Python developers used os.system(), but I'll be honest: it's pretty primitive. It just throws the command at the shell and gives you a return code. If you want to actually use the text the command printed to the screen, os.system() is useless. That's why we use subprocess.run(). It's the modern standard that gives you much more control.
Let's say we want to check if a website is up by pinging it. Instead of writing a complex socket-level network tool in Python, we'll just use the system's ping command.
import subprocess
# We pass the command as a list of strings.
# This is safer than one long string because it avoids shell injection.
result = subprocess.run(["ping", "-c", "1", "google.com"])
print(f"The command exited with code: {result.returncode}")
In this snippet, ["ping", "-c", "1", "google.com"] is our "work order." The returncode is the plumber telling you how it went. In the world of CLI tools, 0 almost always means "Success," and anything else means "Something went wrong."
Actually Listening to the Response
The example above prints the ping results directly to your terminal, but your Python script can't "see" that text. It's just passing through. If you want to capture that output to parse it or save it to a database, you need to tell Python to intercept it using capture_output=True.
I also highly recommend using text=True. By default, subprocess returns raw bytes (because it doesn't know if the external tool is outputting a text file or a binary image). Setting text=True tells Python to decode those bytes into a standard string for you.
import subprocess
# Now we capture the output and treat it as a string
result = subprocess.run(
["ping", "-c", "1", "google.com"],
capture_output=True,
text=True
)
if result.returncode == 0:
print("The server is alive!")
print(f"Here is the detail: {result.stdout}")
else:
print("The server is down or unreachable.")
print(f"Error log: {result.stderr}")
Dealing with Crashes and the 'Shell=True' Trap
Sometimes you don't want to manually check returncode == 0 every single time. You can pass check=True to subprocess.run(). If the command fails, Python will immediately raise a CalledProcessError. It's a cleaner way to handle errors if you're already using try/except blocks in your code.
One last thing: you'll see tutorials telling you to use shell=True. This allows you to pass the command as a single string (like "ls -la | grep .py"). Be very careful here. If you ever pass user-provided input into a command with shell=True, a malicious user could inject their own commands (e.g., adding "; rm -rf /" to the end of a filename). Unless you absolutely need shell features like pipes or environment variables, stick to the list format and keep shell=False (which is the default).
📋 Practical Task
Exercise: Building a System Directory Auditor
Your goal is to create a script that audits a specific folder on your computer to see if any "hidden" files (files starting with a dot) exist. Instead of using Python's os.listdir(), you will use the subprocess module to call the system's directory listing command.
- For macOS/Linux: Use the command
ls -a [path]. - For Windows: Use the command
dir /a [path].
Requirements:
- The script should take a folder path as a variable.
- Use
subprocess.run()to execute the command, capturing the output as text. - The script should then analyze the
stdoutand print the total count of files that start with a dot (.). - Wrap the execution in a
try/exceptblock usingcheck=Trueto handle cases where the provided folder path doesn't exist.
There are no comments for now.