| name | bash |
| description | Language-specific super-code guidelines for bash. |
| risk | safe |
| source | community |
| date_added | 2026-06-16 |
Bash / Shell: Idiomatic Efficiency Reference
Table of Contents
- Quoting & Word Splitting
- Conditionals & Tests
- Loops & Iteration
- Pipes & Process Substitution
- Functions & Return Values
- Error Handling
- Anti-patterns specific to Bash
1. Quoting & Word Splitting {#quoting}
for f in $files; do rm $f; done
for f in "${files[@]}"; do rm -- "$f"; done
path=$(find . -name config)
cat $path
path="$(find . -name config)"
cat "$path"
result=`echo hello`
result=$(echo hello)
if [ $var = "hello" ]; then
if [[ "$var" = "hello" ]]; then
Rule: double-quote every $variable and $(command) unless you specifically need splitting.
2. Conditionals & Tests {#conditionals}
if [ -f "$file" -a -r "$file" ]; then
if [[ -f "$file" && -r "$file" ]]; then
grep -q pattern file
if [ $? -eq 0 ]; then echo "found"; fi
if grep -q pattern file; then echo "found"; fi
if [ "$a" == "$b" ]; then
if [[ "$a" == "$b" ]]; then
if [ "$a" = "$b" ]; then
if [ "$count" -gt 10 ]; then
if (( count > 10 )); then
3. Loops & Iteration {#loops}
for f in $(ls *.txt); do process "$f"; done
for f in *.txt; do
[[ -e "$f" ]] || continue
process "$f"
done
for line in $(cat file.txt); do
while IFS= read -r line; do
process "$line"
done < file.txt
for i in $(seq 1 10); do
for i in {1..10}; do
for (( i = 1; i <= 10; i++ )); do
count=0
cat file.txt | while read -r line; do
(( count++ ))
done
echo "$count"
count=0
while IFS= read -r line; do
(( count++ ))
done < file.txt
echo "$count"
4. Pipes & Process Substitution {#pipes}
grep "error" log.txt | grep "timeout"
grep -E "error.*timeout|timeout.*error" log.txt
awk '/error/ && /timeout/' log.txt
cat file.txt | grep pattern
grep pattern file.txt
cmd1 > /tmp/a.txt
cmd2 > /tmp/b.txt
diff /tmp/a.txt /tmp/b.txt
rm /tmp/a.txt /tmp/b.txt
diff <(cmd1) <(cmd2)
false | true
echo $?
set -o pipefail
false | true
echo $?
5. Functions & Return Values {#functions}
get_name() {
return "Alice"
}
get_name() {
echo "Alice"
}
name=$(get_name)
result=""
compute() { result="done"; }
compute() {
local tmp
tmp=$(do_work)
echo "$tmp"
}
result=$(compute)
function my_func {
my_func() {
6. Error Handling {#errors}
cd /some/dir
rm -rf *
set -euo pipefail
cd /some/dir || { echo "cd failed" >&2; exit 1; }
rm -rf ./*
tmpfile=$(mktemp)
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT
command 2>/dev/null
command 2>/dev/null || true
Start every script with set -euo pipefail. Remove selectively where needed.
7. Anti-patterns specific to Bash {#antipatterns}
| Anti-pattern | Preferred |
|---|
Parsing ls output | glob: for f in *.txt |
cat file | grep | grep pattern file |
Unquoted $var | "$var" always |
[ ] for complex tests | [[ ]] |
| Backtick substitution | $(command) |
$? check after command | if command; then |
echo for debug | printf '%s\n' (portable) |
No set -euo pipefail | always set at script top |
| Temp files without cleanup | trap 'rm -f "$tmp"' EXIT |
eval with user input | avoid; use arrays for dynamic commands |
#!/bin/sh with Bash features | #!/usr/bin/env bash |
String math expr 1 + 1 | $(( 1 + 1 )) |
test -z for number comparison | (( )) for arithmetic |
Limitations
- These are language-specific guidelines and do not cover overall architectural decisions.
- Over-compression might reduce readability; apply judgement.