- 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 ensurepathFedora/RHEL/CentOS (dnf):
sudo dnf install -y curl jq git ShellCheck shfmt python3-pip pipx pipx ensurepathopenSUSE (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 -versionFedora/RHEL/CentOS:
sudo dnf install -y golang go install mvdan.cc/sh/v3/cmd/shfmt@latest ~/.local/go/bin/shfmt -versionopenSUSE:
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 | shPull 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.shReview 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.