Bash / Shell: Idiomatic Efficiency Reference

← Back to skills

1. [Quoting & Word Splitting](#quoting) 2. [Conditionals & Tests](#conditionals) 3. [Loops & Iteration](#loops) 4. [Pipes & Process Substitution](#pipes) 5. [Functions & Return Values](#functions) 6. [Error Handling](#errors) 7. [Anti-patterns specific to Bash](#antipatterns)

Category: General & Miscellaneous
Repo: antigravity-awesome-skills
Path: skills/super-code/bash/SKILL.md
Updated: 6/18/2026, 7:42:54 AM

AI Summary

1. [Quoting & Word Splitting](#quoting) 2. [Conditionals & Tests](#conditionals) 3. [Loops & Iteration](#loops) 4. [Pipes & Process Substitution](#pipes) 5. [Functions & Return Values](#functions) 6. [Error Handling](#errors) 7. [Anti-patterns specific to Bash](#antipatterns). It is useful for general automation, multi-purpose workflows, cross-disciplinary tasks, and utility skills. Source: antigravity-awesome-skills (skills/super-code/bash/SKILL.md).

Bash / Shell: Idiomatic Efficiency Reference

Table of Contents

  1. Quoting & Word Splitting
  2. Conditionals & Tests
  3. Loops & Iteration
  4. Pipes & Process Substitution
  5. Functions & Return Values
  6. Error Handling
  7. Anti-patterns specific to Bash

1. Quoting & Word Splitting {#quoting}

# ❌ Unquoted variable (word splitting + globbing)
for f in $files; do rm $f; done

# ✅
for f in "${files[@]}"; do rm -- "$f"; done
# ❌ Unquoted command substitution
path=$(find . -name config)
cat $path  # breaks on spaces

# ✅
path="$(find . -name config)"
cat "$path"
# ❌ Using backticks for command substitution
result=`echo hello`

# ✅ — $() nests cleanly
result=$(echo hello)
# ❌ String comparison without quotes
if [ $var = "hello" ]; then  # breaks if var is empty or has spaces

# ✅
if [[ "$var" = "hello" ]]; then

Rule: double-quote every $variable and $(command) unless you specifically need splitting.


2. Conditionals & Tests {#conditionals}

# ❌ Single bracket test (POSIX but fragile)
if [ -f "$file" -a -r "$file" ]; then

# ✅ — [[ is safer, supports &&/||, no word splitting inside
if [[ -f "$file" && -r "$file" ]]; then
# ❌ Testing command exit status with if [ $? -eq 0 ]
grep -q pattern file
if [ $? -eq 0 ]; then echo "found"; fi

# ✅ — test command directly
if grep -q pattern file; then echo "found"; fi
# ❌ String equality with == outside [[
if [ "$a" == "$b" ]; then  # == not POSIX in [ ]

# ✅
if [[ "$a" == "$b" ]]; then  # Bash
# or POSIX:
if [ "$a" = "$b" ]; then
# ❌ Arithmetic with [ ] and string comparison
if [ "$count" -gt 10 ]; then

# ✅ — (( )) for arithmetic
if (( count > 10 )); then

3. Loops & Iteration {#loops}

# ❌ Parsing ls output
for f in $(ls *.txt); do process "$f"; done

# ✅ — glob directly
for f in *.txt; do
    [[ -e "$f" ]] || continue  # handle no-match
    process "$f"
done
# ❌ Reading file line-by-line with for
for line in $(cat file.txt); do  # splits on words, not lines

# ✅
while IFS= read -r line; do
    process "$line"
done < file.txt
# ❌ Seq for counting
for i in $(seq 1 10); do

# ✅ — brace expansion (Bash)
for i in {1..10}; do
# or C-style:
for (( i = 1; i <= 10; i++ )); do
# ❌ Processing command output line-by-line with pipe (subshell trap)
count=0
cat file.txt | while read -r line; do
    (( count++ ))  # count resets after loop — subshell
done
echo "$count"  # always 0

# ✅ — redirect, not pipe
count=0
while IFS= read -r line; do
    (( count++ ))
done < file.txt
echo "$count"

4. Pipes & Process Substitution {#pipes}

# ❌ Chained grep | grep for AND
grep "error" log.txt | grep "timeout"

# ✅ — single grep with pattern
grep -E "error.*timeout|timeout.*error" log.txt
# or awk for complex logic:
awk '/error/ && /timeout/' log.txt
# ❌ cat + pipe (UUOC — Useless Use of Cat)
cat file.txt | grep pattern

# ✅
grep pattern file.txt
# ❌ Temp file for diff between commands
cmd1 > /tmp/a.txt
cmd2 > /tmp/b.txt
diff /tmp/a.txt /tmp/b.txt
rm /tmp/a.txt /tmp/b.txt

# ✅ — process substitution
diff <(cmd1) <(cmd2)
# ❌ Ignoring pipe failures (only last command's exit code)
false | true
echo $?  # 0 — false's failure hidden

# ✅
set -o pipefail
false | true
echo $?  # 1

5. Functions & Return Values {#functions}

# ❌ Using return for string values
get_name() {
    return "Alice"  # return is for exit codes (0-255)
}

# ✅ — echo + capture
get_name() {
    echo "Alice"
}
name=$(get_name)
# ❌ Global variables modified inside functions
result=""
compute() { result="done"; }

# ✅ — use local, return via stdout
compute() {
    local tmp
    tmp=$(do_work)
    echo "$tmp"
}
result=$(compute)
# ❌ Function keyword (not POSIX)
function my_func {

# ✅
my_func() {

6. Error Handling {#errors}

# ❌ No error handling — script continues after failure
cd /some/dir
rm -rf *  # if cd fails, deletes from wrong directory

# ✅
set -euo pipefail

cd /some/dir || { echo "cd failed" >&2; exit 1; }
rm -rf ./*
# ❌ No cleanup on exit
tmpfile=$(mktemp)
# ... script might exit early, leaving tmpfile

# ✅ — trap for cleanup
tmpfile=$(mktemp)
trap 'rm -f "$tmpfile"' EXIT
# ❌ Silencing errors blindly
command 2>/dev/null

# ✅ — redirect only when you know what you're suppressing
command 2>/dev/null || true  # explicit: we expect and accept failure

Start every script with set -euo pipefail. Remove selectively where needed.


7. Anti-patterns specific to Bash {#antipatterns}

Anti-patternPreferred
Parsing ls outputglob: for f in *.txt
cat file | grepgrep pattern file
Unquoted $var"$var" always
[ ] for complex tests[[ ]]
Backtick substitution$(command)
$? check after commandif command; then
echo for debugprintf '%s\n' (portable)
No set -euo pipefailalways set at script top
Temp files without cleanuptrap 'rm -f "$tmp"' EXIT
eval with user inputavoid; 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.

Related skills