Shell scripting gets a bad reputation. "It's hard to read." "It's error-prone." "Use Python instead." I disagree.
POSIX shell is one of the most powerful tools you can learn. It's fast, portable, and always available. Every Unix-like system has /bin/sh. You don't need to install runtimes, manage dependencies, or worry about version mismatches. It just works.
POSIX shell runs everywhere: Linux, BSD, macOS, even embedded systems. Write your script once and it works on any POSIX-compliant system. No virtualenvs, no Gemfiles, no package.json. Just a shebang and you're done.
#!/bin/sh # This runs on any POSIX system
Compare that to Python (2 vs 3), Ruby (rbenv/rvm chaos), or Node (nvm, different module systems). Shell doesn't care. It's been stable for decades.
Shell is fast. It's designed to orchestrate programs, not to be a general-purpose language. You pipe data between tools, each doing one thing well. No interpreter startup overhead, no GC pauses, no framework bloat.
For system tasks (file manipulation, process management, text processing), shell is often faster than high-level languages because you're calling optimized C programs (grep, sed, awk) directly.
A good shell script is small. You use existing tools instead of reinventing them. Need to parse JSON? Use jq. Need to download a file? Use curl. Need to process text? Use sed or awk.
This is the Unix philosophy: small, composable tools that do one thing well. Shell is the glue.
Your shell script has zero runtime dependencies beyond coreutils (which every system has). No requirements.txt, no Cargo.toml, no go.mod. It runs on a fresh install. It runs in a minimal container. It runs on a 10-year-old server.
Shell is your working environment. You test commands interactively, then paste them into a script. The feedback loop is instant. No compile step, no "activate this environment first," no IDE setup. Just type and run.
"Shell is hard to read." Only if you write bad shell. Use meaningful variable names, add comments, break long pipelines into functions. Good shell is readable.
"Shell is error-prone."  
Set set -euf at the top of your scripts. Use ShellCheck to catch bugs before you run. Problem solved.
"Shell can't do X." Then call a program that can. That's the point. Shell orchestrates; specialized tools do the work.
"But Python is more powerful!" For complex logic, sure. But most scripts are glue: "download this, extract that, move these files." Shell does this in 10 lines. Python takes 50 and imports three libraries.
If you write shell, use ShellCheck. It's a static analysis tool that catches common mistakes: unquoted variables, useless cats, broken conditionals, portability issues.
# Bad for f in $(ls *.txt); do echo $f done # ShellCheck warns: Don't parse ls. Use globs. # Also: quote your variables. # Good for f in ./*.txt; do [ -e "$f" ] || continue echo "$f" done
ShellCheck makes you a better shell programmer. Run it in CI. Run it in your editor. Treat warnings as errors.
Here's a script that builds a static site (like this one):
#!/bin/sh set -euf SRC="src" OUT="out" rm -rf "$OUT" mkdir -p "$OUT" for md in "$SRC"/*.md; do [ -e "$md" ] || continue base=$(basename "$md" .md) out="$OUT/$base.html" cat template/header.html > "$out" markdown < "$md" >> "$out" cat template/footer.html >> "$out" done echo "Built $(find "$OUT" -name '*.html' | wc -l) pages."
That's it. No Makefile, no npm scripts, no bundler config. Just a shell script you can read and modify.
Shell is great for glue and orchestration. It's not great for:
Know your tools. Shell is one tool in the box. Use it where it shines.
I practice what I preach. Here are some POSIX shell scripts I've written:
All pure shell. All portable. All maintainable.
POSIX shell is underrated. It's fast, portable, minimal, and always available. Learn it well and you won't need a "task runner" fad.
Write clean shell. Use ShellCheck. Keep scripts small. Embrace the Unix philosophy.
Your system already has what you need. Use it.