These Python Tools Replaced My Bash Scripts — And I’m Not Going Back
Bash is powerful — but these Python tools made my scripts easier to write, read, and scale.

I love automation — but I got tired of debugging Bash like I was deciphering ancient runes.
These Python Tools Replaced My Bash Scripts — And I’m Not Going Back
There was a time when I felt like a shell wizard. I could pipe, grep, awk, sed, and curl my way out of just about any situation. Bash scripting was my Swiss Army knife — lightweight, fast, and already there on every Linux machine.
But then the scripts started growing. Error handling became a mess. Maintenance was a nightmare. And don’t even get me started on debugging multi-line if-else
chains and escaping variables inside quoted strings.
That’s when I started replacing my Bash scripts with Python. One by one.
At first, it felt like overkill. Why bring in a full-blown programming language for a few lines of automation?
But now? I’m not going back.
Here are the Python tools and libraries that completely replaced my Bash scripts — and made my life a whole lot easier.
1. subprocess
→ For Shell Command Execution
Instead of chaining commands in a fragile Bash pipeline, Python’s subprocess
gives you full control, safety, and flexibility.
import subprocess
result = subprocess.run(["ls", "-l"], capture_output=True, text=True)
print(result.stdout)
You can:
- Catch errors using
try/except
- Access
stdout
andstderr
separately - Chain commands with logic
- Avoid shell injection risks
Debuggable, secure, and readable. Once you usesubprocess.run()
withcheck=True
, you’ll never go back to blindly hoping your shell command works.
2. Pathlib
→ For File & Directory Manipulation
Goodbye mkdir
, rm
, mv
, and touch
. Python’s pathlib
turns file operations into intuitive, object-oriented code.
from pathlib import Path
logs = Path("/var/log/myapp")
logs.mkdir(parents=True, exist_ok=True)
for log in logs.glob("*.log"):
print(log.name)
Cross-platform, chainable, and no need to remember weird Bash flags like-p
,-f
, or-r
.
3. argparse
→ For Command-Line Interfaces
Instead of parsing $1
, $2
, and $@
with brittle logic, argparse
gives you a full-blown CLI parser.
import argparse
parser = argparse.ArgumentParser(description="Backup script")
parser.add_argument("src")
parser.add_argument("dest")
args = parser.parse_args()
print(f"Backing up {args.src} to {args.dest}")
Help messages, type validation, optional flags, and extensibility — all built-in.
4. rich
/ typer
→ For Polished CLI Tools
Want progress bars, beautiful terminal output, or interactive prompts? Bash is not your friend here. Python is.
With rich
:
from rich.console import Console
console = Console()
console.print("[bold green]Backup completed successfully![/bold green]")
And with typer
, you can create fully interactive CLI tools with almost no effort.
import typer
app = typer.Typer()
@app.command()
def greet(name: str):
print(f"Hello, {name}!")
if __name__ == "__main__":
app()
Type hints + beautiful output = maintainable, scalable CLI apps.
5. schedule
/ APScheduler
→ For Cron Jobs
Instead of editing crontabs or writing one-liner cron jobs, Python libraries like schedule
let you define recurring tasks in code.
import schedule
import time
def job():
print("Running scheduled task...")
schedule.every(10).minutes.do(job)
while True:
schedule.run_pending()
time.sleep(1)
No need to SSH into a server just to tweak a crontab entry. You can test, log, and manage jobs directly in your script.
6. requests
→ For API Calls Instead of curl
I used to do things like:
curl -X POST -H "Authorization: Bearer $TOKEN" -d '{"key":"value"}' https://api.example.com/data
Now, it’s:
import requests
headers = {"Authorization": f"Bearer {token}"}
data = {"key": "value"}
r = requests.post("https://api.example.com/data", headers=headers, json=data)
Readable, error-checked, and works seamlessly with JSON.
7. Logging and Exception Handling — Like a Grown-Up
Bash error handling is like duct-taping your logic together with || exit 1
.
Python gives you structured logging and real exception handling:
import logging
logging.basicConfig(level=logging.INFO)
try:
do_something()
except Exception as e:
logging.error("Something went wrong: %s", e)
Tracebacks, logging levels, and graceful exits — all without the hacks.
Final Thoughts
Bash is great for quick-and-dirty one-liners. But when scripts grow, they become brittle, unreadable, and hard to maintain.
Python, on the other hand, gives you:
— Structure
— Readability
— Portability
— Power
These tools didn’t just replace my Bash scripts — they made them obsolete.
So if you’re still wrangling with .sh
files and set -euo pipefail
, give Python a shot. Your future self will thank you.
You don’t have to pick one over the other. Sometimes I still mix Bash for startup scripts and use Python for the logic-heavy parts.
But for anything serious? I’m all in on Python.
Enjoyed this article?
Follow me for more real-world Python tips, productivity hacks, and developer tools that actually make life easier.