Bash (Bourne Again SHell) is a Unix shell and command language written by Brian Fox for the GNU Project as a free software replacement for the Bourne shell. It is widely used as the default login shell for most Linux distributions and Apples macOS.
Bash Anti-Patterns Overview
Bash, despite its ubiquity in system administration and automation, has several common anti-patterns that can lead to bugs, security vulnerabilities, and maintainability issues. Here are the most important anti-patterns to avoid when writing Bash scripts.
Not Quoting Variables
Always quote your variables in Bash to prevent word splitting and globbing. Unquoted variables can lead to unexpected behavior, especially when they contain spaces, wildcards, or special characters.
Using ls in Scripts
Avoid parsing the output of ls
in scripts. It can break when filenames contain spaces, newlines, or other special characters. Use shell globbing, find
, or other more robust methods instead.
Not Checking Command Exit Status
Always check the exit status of commands, especially those that might fail. Use conditional statements, the set -e
option to exit on any error, or combine both approaches for more robust error handling.
Using eval
Avoid using eval
whenever possible, especially with user input. It can lead to code injection vulnerabilities. If you must use it, validate input strictly and consider alternatives like case statements or function dispatching.
Command Injection Vulnerabilities
Be careful with command arguments that come from user input or external sources. Use --
to indicate the end of options when available, and consider using printf %q
to properly quote arguments for commands that don’t support --
.
Using Backticks Instead of $()
Use $()
for command substitution instead of backticks. It’s more readable, especially when nested, and more consistent with other shell constructs.
Not Setting IFS When Reading Lines
When reading lines from a file, set IFS=
(empty) and use the -r
flag with read
to prevent backslash interpretation. This preserves leading and trailing whitespace and handles backslashes correctly.
Using /bin/sh for Bash Scripts
If your script uses Bash-specific features, use #!/bin/bash
as the shebang, not #!/bin/sh
. On many systems, /bin/sh
might be a different shell (like dash) that doesn’t support Bash features.
Not Using set -u
Use set -u
(or set -o nounset
) to make your script exit when it tries to use an undefined variable. This helps catch typos and missing variables early.
Not Using set -e
Use set -e
(or set -o errexit
) to make your script exit immediately if any command fails. This prevents the script from continuing in an unexpected state after an error.
Not Using Shellcheck
Use Shellcheck to analyze your Bash scripts for common mistakes and pitfalls. It can catch many of the anti-patterns mentioned here and more.
Using [ ] Instead of [[ ]]
Use [[ ]]
instead of [ ]
for conditional tests in Bash. It’s more powerful, safer with empty variables, supports logical operators directly, and provides pattern matching.
Hardcoding Paths
Avoid hardcoding absolute paths in your scripts. Use relative paths, determine paths dynamically, or use environment variables to make your scripts more portable and easier to maintain.
Not Using Functions
Use functions to avoid code duplication and improve maintainability. Functions make your code more modular, easier to understand, and easier to test.
Using echo for Printing to stderr
Use printf
instead of echo
for more consistent behavior across different systems, especially when printing to stderr or when dealing with strings that start with options like -n
or -e
.
Using Uppercase Variable Names
Reserve uppercase variable names for environment variables and constants. Use lowercase for regular variables to avoid conflicts with environment variables and to follow convention.
Not Using Local Variables in Functions
Use local
to declare function-specific variables. This prevents unintended side effects and makes functions more self-contained and reusable.
Using Temporary Files Insecurely
Use mktemp
to create secure temporary files with unique names. Add a trap to clean up temporary files when the script exits, even if it exits due to an error.
Not Using Double Brackets for Regex
Use double brackets [[ ]]
with the =~
operator for regular expression matching. Single brackets [ ]
only support glob patterns, not regular expressions.
Not Using Proper Shebang
Always include a proper shebang line at the beginning of your script. Using #!/usr/bin/env bash
is more portable than #!/bin/bash
because it will find the Bash interpreter in the user’s PATH.
Using Deprecated Syntax
Avoid using deprecated syntax like $[...]
for arithmetic expansion. Use the modern equivalents like $((...))
instead.
Not Using Parameter Expansion
Use parameter expansion for default values, substring operations, and pattern matching. It’s more concise and often more efficient than using conditional statements or external commands.