- Posted on
- • Artificial Intelligence
Build Artificial Intelligence CLI Tools with Bash
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Build Artificial Intelligence CLI Tools with Bash
If you can pipe it, you can AI it.
Most of us already reach for the terminal to grep logs, json-parse APIs, or wire commands together. Yet when we want “a bit of AI,” we leave the shell, paste into a web UI, and lose time and context. What if your usual Bash pipelines could ask, summarize, classify, translate, or transcribe—inline?
This guide shows you how to build practical, shell-native AI tools with Bash and curl. You’ll get:
A minimal, repeatable setup for AI from the command line
3–5 concrete scripts you can drop into your $PATH today
Tips for streaming output, chunking long inputs, and safe configuration
Bring AI to where your work already happens: the terminal.
Why Bash is a great place for AI
Ubiquity and zero-friction: Bash, curl, jq, git—already on your machines and CI.
Composability: Pipe any output into AI and right back out to other tools.
Auditability: Your prompts, outputs, and scripts are plain text you can version control.
Portability: Same scripts across Debian/Ubuntu, Fedora/RHEL, openSUSE, containers, and CI runners.
Speed to prototype: 10–30 lines of Bash can out-iterate weeks of UI work.
1) Install prerequisites
We’ll use curl to call the API and jq to parse JSON. ffmpeg is helpful for audio transcription, and git for the commit message example.
- Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y curl jq ffmpeg git coreutils
- Fedora/RHEL/CentOS (dnf):
sudo dnf install -y curl jq ffmpeg git coreutils
- openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y curl jq ffmpeg git coreutils
Optional but handy:
fzf for fuzzy-finding
ripgrep (rg) for fast searching
2) Configure your environment (one-time)
Set your API settings and keep them out of history.
- Create a config file:
mkdir -p ~/.config/ai
umask 077
cat > ~/.config/ai/env <<'EOF'
# Required: your API key (do not quote)
export AI_API_KEY="PUT-YOUR-KEY-HERE"
# Optional: override the default model or base URL
export AI_MODEL="${AI_MODEL:-gpt-4o-mini}"
export AI_BASE_URL="${AI_BASE_URL:-https://api.openai.com/v1}"
EOF
- Source it from your shell startup:
grep -qxF '[ -f ~/.config/ai/env ] && . ~/.config/ai/env' ~/.bashrc || \
echo '[ -f ~/.config/ai/env ] && . ~/.config/ai/env' >> ~/.bashrc
. ~/.config/ai/env
Security note:
Keep umask 077 for private config files.
Never hardcode keys into scripts or commit them.
Consider separate keys for local dev vs CI.
3) Script: ai.sh — chat from the terminal (streaming)
This is your Swiss Army knife. It takes a prompt from args or STDIN, calls the API, and streams tokens to your terminal.
- Save to ~/bin/ai.sh and make executable:
mkdir -p ~/bin
cat > ~/bin/ai.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
: "${AI_API_KEY:?AI_API_KEY is not set (see ~/.config/ai/env)}"
AI_MODEL="${AI_MODEL:-gpt-4o-mini}"
AI_BASE_URL="${AI_BASE_URL:-https://api.openai.com/v1}"
if [[ $# -gt 0 ]]; then
PROMPT="$*"
else
# Read from STDIN if no args provided
PROMPT="$(cat)"
fi
payload=$(jq -n \
--arg model "$AI_MODEL" \
--arg content "$PROMPT" \
'{
model: $model,
stream: true,
messages: [
{role: "system", content: "You are a concise, shell-native assistant. When possible, answer with plain text optimized for terminals."},
{role: "user", content: $content}
]
}')
curl -sS -N "$AI_BASE_URL/chat/completions" \
-H "Authorization: Bearer '"$AI_API_KEY"'" \
-H "Content-Type: application/json" \
-d "$payload" | \
while IFS= read -r line; do
[[ -z "$line" ]] && continue
[[ "$line" != data:* ]] && continue
payload="${line#data: }"
[[ "$payload" == "[DONE]" ]] && break
# Print streamed token deltas if present
chunk=$(jq -r 'try .choices[0].delta.content // empty' <<<"$payload")
[[ -n "$chunk" ]] && printf "%s" "$chunk"
done
printf "\n"
EOF
chmod +x ~/bin/ai.sh
- Ensure ~/bin is on your PATH:
grep -qxF 'export PATH="$HOME/bin:$PATH"' ~/.bashrc || \
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
. ~/.bashrc
- Try it:
ai.sh "Write a one-liner Bash command that counts unique IPs in access.log"
- Pipe into it:
dmesg | tail -n 200 | ai.sh "Summarize the key warnings:"
4) Script: summarize.sh — fast TL;DR for files/stdin (with chunking)
Summarization is the #1 terminal AI use case. This script:
Reads from files or STDIN
Chunks large input to avoid token limits
Returns a crisp TL;DR
cat > ~/bin/summarize.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
: "${AI_API_KEY:?AI_API_KEY is not set}"
AI_MODEL="${AI_MODEL:-gpt-4o-mini}"
AI_BASE_URL="${AI_BASE_URL:-https://api.openai.com/v1}"
read_all_input() {
if [[ $# -gt 0 ]]; then
cat "$@"
else
cat
fi
}
# Adjust chunk size as needed (bytes)
CHUNK_BYTES="${CHUNK_BYTES:-120000}"
input="$(read_all_input "$@")"
len="${#input}"
summaries=()
offset=0
while (( offset < len )); do
chunk="${input:offset:CHUNK_BYTES}"
offset=$(( offset + CHUNK_BYTES ))
summary=$(curl -sS "$AI_BASE_URL/chat/completions" \
-H "Authorization: Bearer $AI_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg model "$AI_MODEL" \
--arg content "Summarize succinctly for a technical reader. Focus on key points, decisions, and actions.\n\n---\n$chunk" \
'{
model: $model,
messages: [
{role: "system", content: "You produce crisp TL;DRs suitable for terminal display."},
{role: "user", content: $content}
],
temperature: 0.2
}')" \
| jq -r '.choices[0].message.content')
summaries+=("$summary")
done
if (( ${#summaries[@]} == 1 )); then
printf "%s\n" "${summaries[0]}"
else
final_input=$(printf 'Combine the following partial summaries into one concise TL;DR:\n\n- %s\n' "${summaries[@]}")
curl -sS "$AI_BASE_URL/chat/completions" \
-H "Authorization: Bearer $AI_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg model "$AI_MODEL" \
--arg content "$final_input" \
'{
model: $model,
messages: [
{role: "system", content: "Output a single compact TL;DR, 5-8 bullets max."},
{role: "user", content: $content}
],
temperature: 0.2
}')" \
| jq -r '.choices[0].message.content'
fi
EOF
chmod +x ~/bin/summarize.sh
Examples:
man tar | col -b | summarize.sh
journalctl -u nginx --since "1 hour ago" | summarize.sh
summarize.sh README.md docs/*.md
5) Script: transcribe.sh — speech-to-text from the shell
Turn meetings, voice memos, or screen recordings into text.
cat > ~/bin/transcribe.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
: "${AI_API_KEY:?AI_API_KEY is not set}"
AI_BASE_URL="${AI_BASE_URL:-https://api.openai.com/v1}"
file="${1:-}"
if [[ -z "$file" || ! -f "$file" ]]; then
echo "Usage: transcribe.sh path/to/audio.(wav|mp3|m4a|webm)" >&2
exit 1
fi
# Model name may vary; whisper-1 is commonly used for transcription
curl -sS -X POST "$AI_BASE_URL/audio/transcriptions" \
-H "Authorization: Bearer $AI_API_KEY" \
-H "Content-Type: multipart/form-data" \
-F "model=whisper-1" \
-F "file=@${file}" \
| jq -r '.text // .translation // .result // .data // .message // empty'
EOF
chmod +x ~/bin/transcribe.sh
Record audio (Linux, using ffmpeg) and transcribe:
# Record 10 seconds from default input
ffmpeg -f alsa -i default -t 10 out.wav
transcribe.sh out.wav
6) Script: commit-ai — generate commit messages from staged changes
Speed up disciplined commit messages. Pipe your staged diff and ask for a clear, conventional message.
cat > ~/bin/commit-ai <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
: "${AI_API_KEY:?AI_API_KEY is not set}"
AI_MODEL="${AI_MODEL:-gpt-4o-mini}"
AI_BASE_URL="${AI_BASE_URL:-https://api.openai.com/v1}"
diff="$(git diff --cached)"
if [[ -z "$diff" ]]; then
echo "No staged changes." >&2
exit 1
fi
prompt=$'Write a concise Conventional Commit subject and a short body for this diff. Use present tense, <72 char subject, wrap body at 72.\n\n---\n'"$diff"
curl -sS "$AI_BASE_URL/chat/completions" \
-H "Authorization: Bearer $AI_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg model "$AI_MODEL" \
--arg content "$prompt" \
'{
model: $model,
messages: [
{role: "system", content: "You write precise Conventional Commits."},
{role: "user", content: $content}
],
temperature: 0.2
}')" \
| jq -r '.choices[0].message.content'
EOF
chmod +x ~/bin/commit-ai
Use it:
git add -A
commit-ai | tee /tmp/CM.txt
git commit -e -F /tmp/CM.txt
Optional: wire into a repo’s prepare-commit-msg hook:
cat > .git/hooks/prepare-commit-msg <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
[[ -n "${AI_API_KEY:-}" ]] || exit 0
msg_file="$1"
# Only when the message is empty and not a merge
if [[ -s "$msg_file" || "${2:-}" == "merge" ]]; then
exit 0
fi
tmp="$(mktemp)"
commit-ai > "$tmp" || exit 0
cat "$tmp" > "$msg_file"
rm -f "$tmp"
EOF
chmod +x .git/hooks/prepare-commit-msg
Real-world usage patterns
Instant TL;DR for logs:
journalctl -u myservice -n 500 | summarize.shExplain errors:
make 2>&1 | ai.sh "Explain the root cause and fix:"Data cleanup/classification:
cut -d',' -f3 customers.csv | ai.sh "Classify each line as 'lead' or 'customer'. Output CSV: original,label"Docstring or SQL helper:
ai.sh "Given this schema, write a SQL query to find the top 10 active users: ..."Transcribe screen recordings and extract action items:
transcribe.sh meeting.m4a | ai.sh "Extract and number action items:"
Optional: local models with Ollama
If you prefer local inference, you can switch AI_BASE_URL to a local server such as Ollama and set a local model name (e.g., llama3). Installation instructions can change over time; consult the upstream docs. Typical Linux install:
curl -fsSL https://ollama.com/install.sh | sh
Then:
export AI_BASE_URL="http://localhost:11434/v1"
export AI_MODEL="llama3"
Your Bash scripts above continue working since they speak the same HTTP/JSON shape.
Tips for robust Bash + AI
Use strict modes:
set -euo pipefailand check required env vars.Rate limits: add
--retry-all-errors --retry 3 --retry-delay 1for non-streaming curl calls.Privacy: never pipe secrets into third-party APIs. Redact or use local models.
Determinism: for tools that must be stable (e.g., CI), set
temperature: 0–0.2.Logging: tee outputs to a file for traceability in automated jobs.
Conclusion / Call to Action
You don’t need a new GUI to get value from AI—just a few small Bash scripts.
Start with ai.sh for ad-hoc prompts and explainers.
Add summarize.sh to turn any noisy output into a TL;DR.
Use transcribe.sh and commit-ai to integrate AI into your daily flow.
Next steps:
Drop these scripts into your dotfiles repo.
Wrap them into make targets or CI jobs.
Extend with your domain prompts (security, data, SRE, docs).
If you can pipe it, you can AI it. Try one of the examples above right now and watch your terminal get smarter.