When to Reach for Another Language
Bash is at its best gluing programs, files and folders together on any Linux server or Mac; when a script needs real data handling, grows into many files of logic or has to run for Windows users, Python, JavaScript or C# fits better, and a bash script can hand the hard part to a program in one of them.
Journeyman · Unix · Linux · Updated
After this lesson you can
- Name the jobs bash is best at, and the signs that a script should become a program in Python, JavaScript or C#
- Have a bash script hand one job to a program in another language, such as a Python script that does the math
Key takeaways
- Bash is best at gluing programs together, handling files and folders, starting things, and short jobs on any Linux server or Mac.
- Signs a script wants another language: decimals, dates and nested data; hundreds of lines of logic; users on Windows without WSL; a window or a web page.
- Python suits data and reports, JavaScript suits a project that's already a website, and C# suits larger apps and Windows.
- Both can work together: bash runs the steps and calls a Python, JavaScript or C# program for the part bash is bad at.
- #!/bin/sh is the smaller shell found everywhere. Write for it only when bash isn't there, because [[ ]] and arrays aren't.
The bakery's sales script started as twenty lines: read the day's sales, add them up, save a report. A year later it's four hundred. Prices have cents, so it multiplies by 100 before every sum, because $(( )) counts only whole numbers (Values from Commands: $( ) and $(( ))). A cake called cake, birthday broke the CSV reading (CSV Files in Bash, and Their Limits). Every new feature breaks an old one. Nothing is wrong with you or with bash: the script has outgrown the job bash is built for. Knowing when that happens is part of being good at bash, and a developer who can say "this part should be Python" saves a team weeks.
What bash is best at
Bash is a scripting language: a language for directing other programs. That's its strength, and the course has used it all along:
- Gluing programs together: run this, then that, only if it worked, with the output of one fed to the next.
- Files and folders: copying, moving, renaming, backing up, cleaning up.
- Starting things: a dev server, a deploy, a scheduled job, a project's scripts.
- Being there already: every Linux server and every Mac has a shell, with nothing to install.
A deploy script, a backup, a setup.sh: short jobs made of commands. Writing them in another language usually means more lines, and calling the same commands anyway.
Signs a script wants to be a program
None of these is a hard rule. Each one is a reason to stop and ask:
- Real data: decimals, dates and time zones, CSV files with quoted commas, data inside data (an order that holds a list of cakes, each with its own price). Bash's arrays are one level deep, and its math is whole numbers only.
- Lots of logic: hundreds of lines, many functions passing values around, and tests that are hard to write.
- Windows users: a bash script needs bash. A coworker on Windows without WSL can't run it.
- A window or a web page: bash talks in text, in a terminal.
- Speed over many items: a loop that starts a program for each of 100,000 lines takes minutes where a single program takes a second.
Python, JavaScript or C#
These are general-purpose languages, built for programs of any size. Three common choices, each with its strengths:
- Python reads well and has CSV, JSON and date tools built in, so it's a common next step for data and reports. Ubuntu has it already, and on a Mac,
brew install pythoninstalls it. - JavaScript, run with Node.js, fits a project that's already a website, so the team writes one language everywhere.
- C#, run with .NET, suits larger apps and Windows desktops, and its types catch many mistakes before the program runs. Since .NET 10,
dotnet run app.csruns a single file, much like a script.
There's no wrong answer among them: pick the one your team or your class already uses.
One step in the other direction: #!/bin/sh runs a script in the smallest standard shell, written to the POSIX rules that every Unix-like system follows. On Ubuntu it's a smaller shell called dash:
ls -l /bin/sh
lrwxrwxrwx 1 root root 4 Feb 2 2026 /bin/sh -> dash
[[ ]] and arrays aren't part of it. Write for sh only when bash isn't installed, as in some very small containers.
Both together
It's never bash or Python: options, not sides. Keep bash for the steps, and hand the hard part to a program that's good at it. Make the bakery's sales in a practice folder:
mkdir -p ~/terminal-practice/bakery-sales && cd ~/terminal-practice/bakery-sales
printf 'item,price\nsourdough loaf,6.00\nblueberry muffin,3.25\ncinnamon roll,4.50\nbirthday cake,32.00\n' > sales.csv
The math goes to Python. A here document (Options and Flags: getopts and --help) writes total.py without leaving the terminal:
cat > total.py <<'EOF'
import csv
with open("sales.csv") as f:
prices = [float(row["price"]) for row in csv.DictReader(f)]
print(f"{len(prices)} sales, total ${sum(prices):.2f}, average ${sum(prices) / len(prices):.2f}")
EOF
python3 total.py
4 sales, total $45.75, average $11.44
And bash does what it's good at: the folder, the file name with today's date, and running the steps in order. Save this as report.sh and make it runnable:
#!/usr/bin/env bash
# report.sh: saves today's sales report in the reports folder.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"
mkdir -p reports
python3 total.py > "reports/sales-$(date +%F).txt"
echo "Saved reports/sales-$(date +%F).txt"
./report.sh
cat reports/*.txt
Saved reports/sales-2026-11-09.txt
4 sales, total $45.75, average $11.44
Your date will differ. When the report needs more, only total.py grows; report.sh stays the same few lines.
Check yourself
2 questions to try on the lesson page.
So what
Bash is the glue, not the whole machine: let it run the steps, and when a part needs real data handling or real size, give that part to the language built for it.