Tools · Python and Bash for security automation · 12 / 20
Quoting in shell scripts: why "$var" matters and how ShellCheck catches mistakes
See how unquoted variables split on spaces and expand globs, quote correctly, use arrays for argument lists, and run ShellCheck on every script.
Word splitting and globbing
An unquoted $file is split on whitespace and each piece is expanded as a glob pattern. A file named “report 2026.txt” becomes two arguments; a value containing * may expand to every file in the directory. In scripts that delete, move, or upload files, this causes data loss or leaks.
Rules of thumb
Quote every expansion: "$var", "$(command)", "${array[@]}". Use arrays to build argument lists rather than strings. Use -- before file arguments so names starting with a dash are not treated as options. Prefer find -print0 with xargs -0 or while read -r -d "" loops for file lists.
files=("$@")
for f in "${files[@]}"; do
sha256sum -- "$f"
done
args=(-sS --max-time 10 -H "Authorization: Bearer $TOKEN")
curl "${args[@]}" -- "$URL"
find /var/www -type f -name '*.php' -print0 | while IFS= read -r -d '' f; do
printf '%s\n' "$f"
doneLab: break it and lint it
Create a test directory with files named “a b.txt”, “-n”, and “*”. Write a loop with unquoted variables that prints and hashes each file and see what goes wrong. Run ShellCheck, read each warning code (such as SC2086 for unquoted variables), fix them, and run the script again.
mkdir q && cd q && touch 'a b.txt' -- -n '*' printf '%s\n' 'for f in $(ls); do sha256sum $f; done' > ../broken.sh bash ../broken.sh # broken sudo apt install -y shellcheck shellcheck ../broken.sh
Make it routine
Add ShellCheck to your editor and CI so every shell script is linted on change. Most warnings point to real bugs, and the wiki page for each code explains the fix.
Diagram
Unquoted versus quoted
One pair of quotes changes the outcome.
f="old report.txt". rm old and rm report.txt
f="*". rm every file in the directory
f="-rf". Treated as options
f="old report.txt". rm "old report.txt"
f="*". rm the file literally named *
f="-rf". rm the file named -rf
Check yourself
1. What happens to an unquoted variable containing spaces?
2. Which ShellCheck code warns about unquoted variables?
3. What is the safe way to build a list of command arguments in Bash?