- Posted on
- • Artificial Intelligence
Artificial Intelligence Bash Automation Checklist
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence Bash Automation Checklist
Ever copy-pasted a quick curl to an AI API, only to realize “turning this into reliable automation” is a very different game? The difference between a demo and dependable ops is a checklist: secrets, retries, caching, scheduling, and safety. This article gives you a practical, vendor-agnostic Bash automation template plus a short checklist you can apply to any AI workflow.
What you’ll get:
A minimal, robust Bash template for AI API calls (OpenAI-compatible).
A ready-to-use retry/backoff, caching, and logging pattern.
Batch processing and scheduling examples (cron and systemd).
Package installation commands for apt, dnf, and zypper.
Prerequisites (install these first)
curl (HTTP client)
jq (JSON parsing)
direnv (optional, for clean env-var management)
cron/cronie (if you’ll use cron scheduling)
Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y curl jq direnv cron
Fedora/RHEL (dnf):
sudo dnf install -y curl jq direnv cronie
openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y curl jq direnv cron
Enable/Start cron if you plan to use it:
Debian/Ubuntu:
sudo systemctl enable --now cronFedora/RHEL:
sudo systemctl enable --now crondopenSUSE:
sudo systemctl enable --now cron
The AI Bash Automation Checklist (with examples)
Below is a compact, production-minded pattern you can copy into a script, then adapt per task.
1) Baseline: strict mode, env, dependencies, and secrets
Use strict Bash flags.
Keep secrets out of shell history.
Centralize provider config (base URL, model, API key).
Create an env file and lock it down:
mkdir -p ~/.config/ai
cat > ~/.config/ai/env << 'EOF'
# OpenAI-compatible defaults (adjust as needed)
export AI_BASE_URL="https://api.openai.com/v1"
export AI_MODEL="gpt-3.5-turbo" # or another available model
export AI_API_KEY="" # put your key here
# Optional: redact personal data before sending to the API
export AI_REDACT="true"
EOF
chmod 600 ~/.config/ai/env
Optional: auto-load the env with direnv
echo 'source ~/.config/ai/env' > ~/.config/ai/.envrc
direnv allow ~/.config/ai
Script skeleton (ai_automate.sh):
#!/usr/bin/env bash
set -Eeuo pipefail
IFS=$'\n\t'
# Load env and verify dependencies
if [[ -f "${HOME}/.config/ai/env" ]]; then
# shellcheck source=/dev/null
source "${HOME}/.config/ai/env"
fi
need_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "Missing dependency: $1" >&2; exit 1; }; }
need_cmd curl
need_cmd jq
need_cmd mktemp
need_cmd sha256sum || need_cmd shasum # one of them should exist
: "${AI_BASE_URL:?Set AI_BASE_URL in ~/.config/ai/env}"
: "${AI_MODEL:?Set AI_MODEL in ~/.config/ai/env}"
: "${AI_API_KEY:?Set AI_API_KEY in ~/.config/ai/env}"
log() { printf '[%s] %s\n' "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" "$*" >&2; }
redact_if_needed() {
local input
input="$1"
if [[ "${AI_REDACT:-}" == "true" ]]; then
printf '%s' "$input" \
| sed -E 's/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/[redacted-email]/g' \
| sed -E 's/\+?[0-9][0-9 .-]{7,}[0-9]/[redacted-phone]/g'
else
printf '%s' "$input"
fi
}
Why: strict mode prevents silent failures; centralized env simplifies provider swaps; redaction helps reduce risk when sending logs or PII.
2) A robust AI request function (retries, backoff, JSON-safe)
Build JSON safely via jq.
Implement exponential backoff for 429/5xx.
Extract the assistant’s content reliably.
Add to ai_automate.sh:
ai_chat() {
local system_prompt="$1"
local user_prompt="$2"
local max_retries="${3:-5}"
user_prompt="$(redact_if_needed "$user_prompt")"
local payload
payload="$(jq -n \
--arg model "$AI_MODEL" \
--arg sys "$system_prompt" \
--arg usr "$user_prompt" \
'{model:$model, messages:[{role:"system",content:$sys},{role:"user",content:$usr}] }'
)"
local attempt=0 sleep_seconds=1
while (( attempt <= max_retries )); do
local tmp
tmp="$(mktemp)"
local http_code
http_code="$(curl -sS -o "$tmp" -w '%{http_code}' \
-H "Authorization: Bearer ${AI_API_KEY}" \
-H "Content-Type: application/json" \
-X POST "${AI_BASE_URL}/chat/completions" \
--data "$payload" \
--max-time 60 \
)" || http_code=0
if [[ "$http_code" =~ ^2 ]]; then
# Parse the content field
jq -r '.choices[0].message.content // empty' < "$tmp"
rm -f "$tmp"
return 0
fi
# 429 or 5xx: backoff and retry
if [[ "$http_code" == "429" || "$http_code" =~ ^5 ]]; then
log "HTTP $http_code - retrying in ${sleep_seconds}s (attempt $((attempt+1))/$max_retries)"
sleep "$sleep_seconds"
sleep_seconds=$(( sleep_seconds * 2 ))
attempt=$(( attempt + 1 ))
rm -f "$tmp"
continue
fi
log "Request failed (HTTP $http_code): $(cat "$tmp")"
rm -f "$tmp"
return 1
done
log "Max retries exceeded."
return 1
}
Why: curl alone isn’t enough—retries and timeouts protect you from transient failures and rate limits. jq ensures your JSON is always valid and parses results safely.
3) Caching to control cost and speed
Hash the prompt and model to a cache filename. Reuse results if the cache exists.
Add to ai_automate.sh:
CACHE_DIR="${CACHE_DIR:-${HOME}/.cache/ai}"
mkdir -p "$CACHE_DIR"
cache_key() {
local key_material="$1"
# Prefer sha256sum; fallback to shasum if needed
if command -v sha256sum >/dev/null 2>&1; then
printf '%s' "$key_material" | sha256sum | awk '{print $1}'
else
printf '%s' "$key_material" | shasum -a 256 | awk '{print $1}'
fi
}
ai_chat_cached() {
local system_prompt="$1"
local user_prompt="$2"
local key
key="$(cache_key "${AI_MODEL}__${system_prompt}__${user_prompt}")"
local path="${CACHE_DIR}/${key}.txt"
if [[ -f "$path" ]]; then
cat "$path"
return 0
fi
local out
out="$(ai_chat "$system_prompt" "$user_prompt")" || return 1
printf '%s' "$out" > "$path"
printf '%s' "$out"
}
Why: Simple file caching can slash costs and latency for repeated prompts, CI runs, and flaky networks.
4) Real-world batch example: summarize Markdown files
Create a small prompt template.
Batch over files with find/xargs.
Cache ensures unchanged files don’t re-trigger API calls.
Create a prompt:
cat > summarize.prompt << 'EOF'
You are a concise technical editor. Summarize the following Markdown file into 3 bullet points for an engineering changelog:
- Stay under 80 words.
- Preserve key commands or numbers.
EOF
Add a function to ai_automate.sh:
summarize_file() {
local file="$1"
local sys
sys="$(cat summarize.prompt)"
local usr="Summarize this file. Filename: ${file}\n\n---\n$(cat "$file")"
ai_chat_cached "$sys" "$usr"
}
Run a batch:
chmod +x ./ai_automate.sh
./ai_automate.sh <<'EOS'
set -Eeuo pipefail
IFS=$'\n\t'
# Example: write summaries next to source files
while IFS= read -r -d '' f; do
out="${f%.md}.summary.txt"
printf 'Summarizing: %s -> %s\n' "$f" "$out" >&2
./ai_automate.sh summarize_file "$f" > "$out"
done < <(find . -type f -name '*.md' -print0)
EOS
Alternative: call the function directly if you source the script, e.g. source ai_automate.sh; summarize_file README.md.
Why: This pattern generalizes to translation, code review hints, doc normalization, or model-to-model conversions.
5) Schedule and observe: cron or systemd timers
Option A—cron: run every night at 01:30
crontab -e
Add:
30 1 * * * cd /path/to/project && /usr/bin/env bash -lc 'source ~/.config/ai/env; ./ai_automate.sh batch' >> logs/ai-batch.log 2>&1
Note: bash -lc ensures login semantics so direnv or your shell rc can load env.
Option B—systemd timer: more control and logging Create a unit:
sudo tee /etc/systemd/system/ai-batch.service >/dev/null <<'EOF'
[Unit]
Description=Nightly AI batch summaries
[Service]
Type=oneshot
WorkingDirectory=/path/to/project
EnvironmentFile=%h/.config/ai/env
ExecStart=/usr/bin/bash -lc './ai_automate.sh batch'
StandardOutput=append:/path/to/project/logs/ai-batch.log
StandardError=append:/path/to/project/logs/ai-batch.err
EOF
Timer (01:30 daily):
sudo tee /etc/systemd/system/ai-batch.timer >/dev/null <<'EOF'
[Unit]
Description=Run AI batch nightly
[Timer]
OnCalendar=*-*-* 01:30:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now ai-batch.timer
systemctl list-timers --all | grep ai-batch
Why: cron is everywhere; systemd timers add persistence and structured logs. Pick your poison.
Putting it together: script entry point
You can make your script subcommand-driven:
# At bottom of ai_automate.sh
case "${1:-}" in
summarize_file)
shift; summarize_file "$@"
;;
batch)
# Example batch job; adjust as needed
while IFS= read -r -d '' f; do
out="${f%.md}.summary.txt"
log "Summarizing: $f -> $out"
summarize_file "$f" > "$out"
done < <(find . -type f -name '*.md' -print0)
;;
*)
cat <<USAGE
Usage:
$(basename "$0") summarize_file path/to/file.md
$(basename "$0") batch
USAGE
;;
esac
Conclusion and next steps
With a small checklist—strict mode, env/secrets, retries, caching, and scheduling—you turn a one-off AI curl into a safe, repeatable automation. Start by:
1) Installing curl and jq (see commands above).
2) Creating ~/.config/ai/env with your provider details.
3) Dropping the functions into ai_automate.sh and running a small batch.
4) Adding a cron or systemd timer once you trust it.
Pro tip: keep prompts in versioned files, log inputs/outputs (minus secrets), and measure cache hit rates to control cost.
Want more? Extend the template to:
Stream tokens for TUI feedback.
Add embeddings endpoints for search pipelines.
Swap to local models via an OpenAI-compatible server.
Your shell can do more than you think—automate it.