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 cron
    
  • Fedora/RHEL:

    sudo systemctl enable --now crond
    
  • openSUSE:

    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.