Choosing a shell for daily work
Shell selection is one of those things people overthink until they've been burning CPU time on bad tooling for six months straight. I'll lay out what actually matters in practice, skip the ideology, and show you where the edges cut you.The first question you need to answer is what you're optimizing for. Speed of execution, readability of config, ecosystem maturity, and how much friction you want when you hit an unfamiliar machine. Those four axes rarely align. Pick three, leave one out.
What to prioritize during shell selection
I work mostly in Linux infra, some macOS client work, and a heavy amount of scripting across both. Over the last decade I've cycled through bash, zsh, fish, dash, ksh93, and a few custom dash hybrids. The pattern that kept reappearing was simple: you optimize for the workflow you have today, not the one you imagine yourself doing next month. Bash is the baseline. It's everywhere by default, POSIX-compliant, and its quirks are well-documented. If you're writing deployment scripts or running anything in a container without root access to install packages, bash is your safest bet. The config pain is real, but you won't fight it alone. Nobody will blame you for using it.
Zsh gets recommended constantly, and for good reasons. The completion system is stronger out of the box, plugins exist for nearly every common workflow, and oh-my-zsh lowered the adoption barrier to almost zero. The trade-off is that you're now carrying a dependency chain. Themes, plugins, custom functions — they all load into every new session. If your profile becomes unreadable after six months of additions, you haven't managed your config, you've accumulated debt. Fish is the one most senior engineers dismiss until they try it and realize they've been compensating for missing features their entire career. Autosuggestions, syntax highlighting, and native tab completion without a plugin manager are genuinely useful. The catch is that fish uses a different scripting language than bash. Anything you write for fish won't run in a standard shell pipeline unless you deliberately make it compatible, which limits portability. I once spent two hours debugging a deploy script that worked on my local machine but failed silently on CI because fish's parameter expansion behaves differently than bash's under strict mode. That was a hard lesson in not conflating personal convenience with team compatibility.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Dash is worth mentioning because it exists and it's fast. It's the default shell on Debian-based systems for exactly that reason. If you're writing scripts meant to run on minimal images, use dash. Don't write zsh-isms in a dash script and wonder why it fails. Here's something most guides won't tell you: the differences between these shells matter far less than your understanding of POSIX fundamentals. A shell is a tool for launching processes and piping data. Everything else is packaging. If you can write portable scripts that avoid bash arrays, zsh globs, and fish-specific syntax, you can work in any environment without friction.
My current setup is zsh for interactive use and bash for scripts. I manage zsh through a curated set of plugins, not oh-my-zsh, because I stopped trusting opinionated frameworks to stay maintainable. The config sits at about 120 lines and loads in under 200 milliseconds on cold start. Fish was faster to set up initially, but the portability tax added up faster than I expected. Common pitfall: installing a theme that runs code on prompt redraw. Every keystroke in certain configurations can trigger file system checks or network requests. This feels fine on a fast SSD until you're working over SSH with high latency. Test your prompt under real conditions, not just on your local machine with a cold cache.
Another one people miss: alias expansion happens before command parsing in most shells, which means conditional aliases behave unexpectedly. I ran into this when someone on my team used an alias that expanded a flag into a multi-word argument. It worked in their interactive shell but broke in any non-interactive invocation because alias expansion is disabled by default there. The fix was switching to a function. Functions are more verbose but far more predictable across contexts. If you're starting fresh today and want minimal friction, install zsh, add powerlevel10k for the prompt, and keep your config under 150 lines. Write all your scripts in bash with #!/bin/bash and strict mode enabled. Don't overcomplicate it. The goal is to spend less time configuring your environment and more time doing actual work.