test, [, and [[ (2020)
Shell programmers weigh the trade-offs between POSIX-standard `[ / test` and Bash-specific `[[` for conditionals, highlighting how subtle syntax rules, quoting pitfalls, and boolean logic quirks can easily introduce bugs. Many argue for sticking to portable, Bourne-compatible `sh` when scripts must run on diverse or constrained systems (e.g. BSD, embedded, minimal Docker images), while others favor Bash, zsh or ksh features for convenience in controlled environments. The conversation broadens into whether complex shell scripts should be replaced by “real” languages like Python or Perl, the role of tools such as ShellCheck and `set -euo pipefail`, and how long-term portability versus developer productivity should be balanced.
Shell choice and portability
- Major split between “just use Bash/[[” and “target POSIX sh.”
- Pro-Bash side:
[[is clearer and more powerful; many environments they control have bash, zsh, or ksh anyway. Installing bash is seen as low effort in many contexts. - Portability side: bash is not always present (BSDs, embedded systems, minimal Docker images, OpenWRT, busybox-only setups, locked-down customer machines, macOS with ancient bash).
- Writing Bourne/POSIX sh is described as part of professional maturity when scripts run on unknown or legacy systems.
- Some argue portability has a cost and shouldn’t be a universal goal; for in-house scripts it’s fine to lean on bashisms.
[, test, and [[
[,testare POSIX and usually binaries or builtins;[[is shell-specific (bash, zsh, ksh, etc.).- Several prefer
testover[to reinforce that it’s a command, not syntax;[’s closing]and look-alike “syntax” are seen as misleading hacks. - Others say
[[is preferable when you’ve already committed to Bash because of safer semantics (e.g., regex, pattern matching, fewer quoting pitfalls). - Some insist on using only POSIX features even when shells support
[[; others say that’s needless “worship of portability.”
Common footguns and techniques
- Biggest pitfall: unquoted variables with
test/[(empty or space-containing values, single-argument behavior). Repeated advice: “quote your variables” and use tools like ShellCheck andset -euo pipefail. - Using
&&/||asifshorthand is popular but has different failure semantics when the post-&&command fails. -a/-oinsidetestare noted as obsolescent; chaining separate tests with&&/||is preferred.- Boolean logic is awkward: no real boolean type; workarounds use exit codes or numeric 0/1.
When to use shell vs another language
- Several argue complex logic should move to a “real” language (Python, Perl, Node/zx, etc.); shell is best for glue, orchestration, and integration tests.
- Others defend shell as a “real language” if used with discipline, but still wouldn’t start large projects in it.
Standards, GNUisms, and ecosystem quirks
- Debate over sticking to POSIX vs exploiting GNU/Bash extensions (
pipefail, GNUgrep/sedflags, etc.). - POSIX viewed as valuable for interoperability but also as lagging real-world needs.
- Various side notes: dash as
/bin/shon Debian/Ubuntu, macOS BSD vs GNU tools, autoconf’s special treatment of[], odd behaviors of$_, and busybox symlink vs hardlink layouts.