Posted on
Artificial Intelligence

Artificial Intelligence Bash Code Reviews

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

Artificial Intelligence Bash Code Reviews: Faster, Safer Shell Scripts

Ever merged a “quick” Bash fix that later nuked a server, a cron job, or prod data? Traditional code reviews help, but Bash often slips through with edge cases, quoting errors, or nondeterministic behavior. The good news: pairing classic static tools with an AI reviewer can catch intent-level mistakes, explain risks, and even suggest safe rewrites—right from your terminal.

This post shows you how to add AI-assisted code reviews to your Bash workflow, why it’s worth doing, and how to automate it with practical, low-friction steps. You’ll get copy-paste commands for apt, dnf, and zypper.

Why AI Code Reviews for Bash?

  • Bash is brittle. Word-splitting, globbing, subshells, traps, and error handling are easy to misuse.

  • Static tools (ShellCheck, shfmt) are must-haves, but they don’t understand your intent, domain rules, or deployment context.

  • AI is good at “reasoning about intent,” reviewing diffs for regressions, and providing human-readable justifications—especially valuable when your team is small or under time pressure.

  • Combined workflow = fewer footguns, better explanations, and repeatable quality gates.

What you’ll build

  • A baseline static check (ShellCheck + shfmt)

  • A terminal function to ask an AI model (cloud or local) for a Bash code review

  • A consistent review rubric so the AI focuses on the right risks

  • An optional Git hook to automate reviews on changed scripts

Install the essentials

You’ll need curl, jq, git, ShellCheck, and shfmt. If shfmt isn’t in your repo, see the Go-based fallback below.

  • Debian/Ubuntu (apt):

    sudo apt-get update
    sudo apt-get install -y curl jq git shellcheck shfmt python3-pip pipx
    pipx ensurepath
    
  • Fedora/RHEL/CentOS (dnf):

    sudo dnf install -y curl jq git ShellCheck shfmt python3-pip pipx
    pipx ensurepath
    
  • openSUSE (zypper):

    sudo zypper refresh
    sudo zypper install -y curl jq git ShellCheck shfmt python3-pip pipx
    pipx ensurepath
    

If shfmt is unavailable in your repos, install via Go:

  • Debian/Ubuntu:

    sudo apt-get update
    sudo apt-get install -y golang-go
    go install mvdan.cc/sh/v3/cmd/shfmt@latest
    ~/.local/go/bin/shfmt -version
    
  • Fedora/RHEL/CentOS:

    sudo dnf install -y golang
    go install mvdan.cc/sh/v3/cmd/shfmt@latest
    ~/.local/go/bin/shfmt -version
    
  • openSUSE:

    sudo zypper install -y go
    go install mvdan.cc/sh/v3/cmd/shfmt@latest
    ~/.local/go/bin/shfmt -version
    

Optional: pre-commit (installed via pipx)

pipx install pre-commit
pre-commit --version

Choose your AI backend

Option A: Cloud API (OpenAI-compatible)

  • Export your API key and pick a model: export OPENAI_API_KEY="sk-..." export OPENAI_BASE_URL="https://api.openai.com/v1" export OPENAI_MODEL="gpt-4o-mini"

Option B: Local model with Ollama

  • Install Ollama:

    curl -fsSL https://ollama.com/install.sh | sh
    
  • Pull a code-capable model (examples):

    ollama pull qwen2.5-coder
    # or: ollama pull codellama
    export OLLAMA_MODEL="qwen2.5-coder"
    

Step 1 — Baseline static checks (fast, deterministic)

Add a tiny script you can run before asking AI:

#!/usr/bin/env bash
set -euo pipefail

shfmt_bin="$(command -v shfmt || echo "${HOME}/.local/go/bin/shfmt")"

if [[ $# -eq 0 ]]; then
  echo "Usage: $0 path/to/script.sh [more.sh ...]" >&2
  exit 1
fi

for f in "$@"; do
  echo "==> Formatting: $f"
  "$shfmt_bin" -w -i 2 -ci -bn "$f"
  echo "==> ShellCheck: $f"
  shellcheck -x "$f"
done

echo "Static checks passed."

Run it:

bash tools/static_check.sh myscript.sh

Step 2 — Add a reusable AI review function

This function pipes your script or diff to either a cloud API (OpenAI-compatible) or Ollama, returning a structured review. Save it in your shell profile or a project-local script.

#!/usr/bin/env bash
# ai_review.sh
set -euo pipefail

read_file_or_stdin() {
  if [[ $# -gt 0 ]]; then
    cat "$@"
  else
    cat
  fi
}

make_prompt() {
  cat <<'EOF'
You are reviewing a Bash script. Return a concise, actionable review.

Rubric:

- Safety: quoting, word-splitting, globbing, set -euo pipefail, traps, IFS, sudo/rm risks

- Robustness: error handling, exit codes, portability (/bin/sh vs bash), race conditions

- Performance: unnecessary forks, subshells, external tools overuse

- Maintainability: readability, functions, comments, naming, consistent style

- Idempotence: re-runs, partial failures, file clobbering

- Security: secret handling, filenames with spaces/newlines, injection, permissions

Output format:

- Summary (2–4 lines)

- Top 5 issues with code references

- Suggested fixes or rewritten snippets

- Any test cases to add
EOF
}

ai_review_cloud() {
  local input="$1"
  local prompt
  prompt="$(make_prompt)"

  : "${OPENAI_API_KEY:?Set OPENAI_API_KEY}"
  : "${OPENAI_BASE_URL:=https://api.openai.com/v1}"
  : "${OPENAI_MODEL:=gpt-4o-mini}"

  curl -sS "${OPENAI_BASE_URL}/chat/completions" \
    -H "Authorization: Bearer ${OPENAI_API_KEY}" \
    -H "Content-Type: application/json" \
    -d @- | jq -r '.choices[0].message.content' <<JSON
{
  "model": "${OPENAI_MODEL}",
  "temperature": 0.2,
  "messages": [
    {"role": "system", "content": "You are a meticulous Bash code reviewer."},
    {"role": "user", "content": ${input|jq -Rs .}},
    {"role": "user", "content": ${prompt|jq -Rs .}}
  ]
}
JSON
}

ai_review_ollama() {
  local input="$1"
  local prompt
  prompt="$(make_prompt)"
  : "${OLLAMA_MODEL:?Set OLLAMA_MODEL or unset to use cloud}"

  # Use ollama's chat interface for better formatting
  curl -sS http://localhost:11434/api/chat \
    -H "Content-Type: application/json" \
    -d @- | jq -r '.message.content' <<JSON
{
  "model": "${OLLAMA_MODEL}",
  "stream": false,
  "messages": [
    {"role": "system", "content": "You are a meticulous Bash code reviewer."},
    {"role": "user", "content": $(jq -Rs . <<<"$input")},
    {"role": "user", "content": $(jq -Rs . <<<"$prompt")}
  ]
}
JSON
}

main() {
  local content
  content="$(read_file_or_stdin "$@")"

  # Optional: redact common secrets before sending to AI
  content="$(sed -E 's/(AWS|GCP|AZURE|OPENAI)_[A-Z_]*=([^\s]+)/\1_REDACTED=***REDACTED***/g' <<<"$content")"
  content="$(sed -E 's/(password|token|secret)=[^ ]+/ \1=***REDACTED***/Ig' <<<"$content")"

  if [[ -n "${OLLAMA_MODEL:-}" ]]; then
    ai_review_ollama "$content"
  else
    ai_review_cloud "$content"
  fi
}

main "$@"

Usage examples:

  • Review a file:

    bash ai_review.sh myscript.sh
    
  • Review only what changed (last commit):

    git diff HEAD~1 -- '*.sh' | bash ai_review.sh
    

Step 3 — Give the AI a good brief (prompt template)

A strong prompt gets strong reviews. The function above includes a rubric that focuses the model on Bash-specific risks. Customize it for your environment, e.g.:

  • Target distro and shell versions

  • Must follow set -euo pipefail

  • Require POSIX sh compatibility (if /bin/sh)

  • Disallow curl | sh or require checksum verification

  • Performance budget (e.g., avoid spawning 1000 subshells)

You can edit make_prompt() to encode your standards so every review is consistent.

Step 4 — Automate with Git hooks

Option A: Native pre-commit hook

#!/usr/bin/env bash
# .git/hooks/pre-commit
set -euo pipefail

shfmt_bin="$(command -v shfmt || echo "${HOME}/.local/go/bin/shfmt")"

changed_sh_files=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.sh$' || true)
[[ -z "$changed_sh_files" ]] && exit 0

echo "[pre-commit] Formatting and linting..."
for f in $changed_sh_files; do
  "$shfmt_bin" -w -i 2 -ci -bn "$f"
  shellcheck -x "$f"
  git add "$f"
done

echo "[pre-commit] AI review (summarized)..."
git diff --cached | bash ai_review.sh | sed -n '1,120p' || true

Make it executable:

chmod +x .git/hooks/pre-commit

Option B: pre-commit framework

  • Create .pre-commit-config.yaml:

    repos:
    - repo: local
      hooks:
        - id: shfmt
          name: shfmt
          entry: shfmt -w -i 2 -ci -bn
          language: system
          files: \.sh$
        - id: shellcheck
          name: shellcheck
          entry: shellcheck -x
          language: system
          files: \.sh$
        - id: ai-review
          name: ai-review
          entry: bash ai_review.sh
          language: system
          pass_filenames: false
          stages: [commit]
    
  • Install:

    pre-commit install
    

Step 5 — Real-world example

Buggy script:

#!/usr/bin/env bash
# deploy.sh
ENV=$1
rm -rf /var/www/$ENV/*
cp -r site/* /var/www/$ENV/
echo Deployed to $ENV

Run the review:

bash ai_review.sh deploy.sh

Typical AI findings:

  • Safety: Unquoted $ENV allows word-splitting; rm -rf on constructed path is dangerous if $ENV is empty; no set -euo pipefail.

  • Robustness: No checks that /var/www/$ENV exists or is writable; cp without -a loses perms/timestamps.

  • Suggested rewrite:

    #!/usr/bin/env bash
    set -euo pipefail
    env="${1:-}"
    if [[ -z "$env" ]]; then
    echo "Usage: $0 ENV" >&2
    exit 1
    fi
    
    target="/var/www/${env}"
    if [[ ! -d "$target" ]]; then
    echo "Target dir not found: $target" >&2
    exit 1
    fi
    
    # safer removal: only inside target and guarded
    find "$target" -mindepth 1 -maxdepth 1 -print0 | xargs -0r rm -rf --
    cp -a site/. "$target/"
    echo "Deployed to $env"
    

Tips for success

  • Keep secrets out of prompts. Use redaction (as shown) and avoid pasting configs with tokens.

  • Start with static tools. Fail fast on formatting/lint; ask AI only when needed or on diffs to save tokens/time.

  • Prefer local models when possible (Ollama) for privacy; use cloud for tough reviews.

  • Cache context. For multi-file changes, send only the diff or relevant functions to keep responses focused.

  • Record decisions. Commit a REVIEW.md with accepted AI suggestions and rationale.

Troubleshooting

  • “command not found: shfmt” — install via Go fallback shown above.

  • “401 Unauthorized” on cloud calls — ensure OPENAI_API_KEY is exported and valid.

  • Ollama errors — ensure the daemon is running: ollama serve (usually auto-starts after install).

  • jq not found — confirm it’s installed via your package manager.

Conclusion and Call to Action

AI won’t replace human judgment—but it will catch risky Bash patterns, explain tradeoffs, and speed up reviews. Add the static check script, drop ai_review.sh into your repo, and wire up a pre-commit hook. Start with one critical script and expand from there.

Next steps:

  • Install the tools with your package manager above.

  • Add ai_review.sh to your project and try it on a recent diff.

  • Tune the rubric to your org’s standards, then enable the Git hook.

If you found this useful, share your rubric and pre-commit setup with your team—your future self (and prod) will thank you.