- Posted on
- • Artificial Intelligence
Few-Shot Prompting
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Few‑Shot Prompting from the Linux Command Line: Teach by Example, No Training Required
What if your Bash one‑liners could “learn” a new task in seconds—without training a model, installing big frameworks, or writing Python? That’s the promise of few‑shot prompting: you show an LLM a handful of input→output examples in your prompt, and it generalizes the pattern on the fly. From cleaning filenames to classifying logs, you can wire this directly into your shell with a little curl and jq.
In this post, you’ll learn what few‑shot prompting is, why it works, and how to use it in Bash with practical patterns and ready‑to‑run snippets.
Why Few‑Shot Prompting Is Worth Your Shell Time
It reduces “prompt brittleness.” Examples anchor the model to your task and style, often cutting hallucinations.
It enforces output formats. With a few examples plus “respond only in JSON,” you can parse results reliably.
It saves compute and engineering. No finetuning, just good examples—perfect for quick CLI automation.
It’s iterative. Update examples as you learn; your scripts evolve without redeploying models.
Prerequisites (Install via apt, dnf, zypper)
You’ll use curl to call an OpenAI‑compatible API and jq to build/parse JSON. Many distros ship curl by default, but here are explicit installs.
- Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y curl jq
- Fedora/RHEL/CentOS (dnf):
sudo dnf install -y curl jq
- openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y curl jq
Set your environment (replace with your actual key and model):
export OPENAI_API_KEY="YOUR_KEY"
export OPENAI_BASE_URL="https://api.openai.com/v1" # OpenAI-compatible endpoint
export MODEL="gpt-4o-mini" # Or another chat model
A Tiny Bash Client for Few‑Shot Prompts
This function sends a messages array with few‑shot examples. It uses jq -n to safely craft JSON.
ai_chat() {
local messages_json="$1" # prebuilt messages array as JSON
curl -sS -X POST "$OPENAI_BASE_URL/chat/completions" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d "$(jq -n \
--argjson messages "$messages_json" \
--arg model "${MODEL:-gpt-4o-mini}" \
--argjson temp 0.2 \
'{model: $model, temperature: $temp, messages: $messages}')" \
| jq -r '.choices[0].message.content'
}
Tip: Wrap your system instruction, few‑shot pairs, and your live query into a single messages array.
1) Teach the Model with Clear, Minimal Examples
Start with a strong instruction and 2–3 representative examples. Keep inputs and outputs aligned and consistent.
Example: Safe, deterministic filename slugifier.
slugify() {
local input="$*"
local system="You convert strings into safe, lowercase, kebab-case filenames.
- Replace spaces/underscores with hyphens.
- Remove non-alphanumerics except hyphen and dot.
- Collapse multiple hyphens.
- Trim hyphens from ends.
Output only the filename string."
# Few-shot examples (user/assistant pairs)
local ex1_in="Holiday Photos 2025!.zip"
local ex1_out="holiday-photos-2025.zip"
local ex2_in="README_FINAL (v2).md"
local ex2_out="readme-final-v2.md"
local ex3_in="my__Project__alpha.tar.gz"
local ex3_out="my-project-alpha.tar.gz"
local messages
messages="$(jq -n \
--arg s "$system" \
--arg u1 "$ex1_in" --arg a1 "$ex1_out" \
--arg u2 "$ex2_in" --arg a2 "$ex2_out" \
--arg u3 "$ex3_in" --arg a3 "$ex3_out" \
--arg u4 "$input" \
'{messages: [
{role:"system", content:$s},
{role:"user", content:$u1},
{role:"assistant", content:$a1},
{role:"user", content:$u2},
{role:"assistant", content:$a2},
{role:"user", content:$u3},
{role:"assistant", content:$a3},
{role:"user", content:$u4}
]} | .messages')"
ai_chat "$messages"
}
# Usage:
# slugify "Q4 Report__Draft (Reviewed).pdf"
Why it works:
Clear system instruction sets rules.
Few-shot pairs constrain style and edge cases.
Temperature is low for repeatability.
2) Enforce a Parseable Schema (JSON‑only Outputs)
When you need to integrate with other tools, demand strict JSON. Combine few‑shot examples with an explicit schema.
Example: Lightweight log classifier that returns a stable JSON object.
classify_log() {
local line="$*"
local system="You are a log line classifier.
Return ONLY compact JSON: {\"severity\":\"INFO|WARN|ERROR\",\"reason\":\"string\"}.
Use consistent casing. No extra text."
local ex1_u="2026-05-01 12:00:00 connection established to db"
local ex1_a='{"severity":"INFO","reason":"successful connection message"}'
local ex2_u="disk space critically low: 2% remaining on /var"
local ex2_a='{"severity":"ERROR","reason":"critical resource exhaustion"}'
local ex3_u="retrying request due to timeout"
local ex3_a='{"severity":"WARN","reason":"transient failure, retry attempt"}'
local messages
messages="$(jq -n \
--arg s "$system" \
--arg u1 "$ex1_u" --arg a1 "$ex1_a" \
--arg u2 "$ex2_u" --arg a2 "$ex2_a" \
--arg u3 "$ex3_u" --arg a3 "$ex3_a" \
--arg u4 "$line" \
'{messages: [
{role:"system", content:$s},
{role:"user", content:$u1},
{role:"assistant", content:$a1},
{role:"user", content:$u2},
{role:"assistant", content:$a2},
{role:"user", content:$u3},
{role:"assistant", content:$a3},
{role:"user", content:$u4}
]} | .messages')"
ai_chat "$messages"
}
# Example:
# classify_log "warning: API quota nearing limit"
# ...then parse:
# classify_log "warning: API quota nearing limit" | jq -r '.severity'
Why it works:
Explicit schema + strict “JSON only” instruction.
Examples demonstrate exact key names and values.
Easy to pipe into
jqfor downstream logic.
3) Control Style and Constraints with Rubrics
You can enforce length, voice, or format using a rubric in the system message, then show one or two examples that obey it.
Example: Summarize a diff into a conventional commit.
summarize_commit() {
local diff_text
diff_text="$(cat)" # read from stdin
local system="Summarize changes into a conventional commit.
Rules:
- Start with type(scope): subject
- subject <= 72 chars, lowercase imperative
- No body unless necessary, no trailing period
Types: feat, fix, docs, chore, refactor, test"
local ex1_u=$'Files changed:\n- src/auth/login.sh: add MFA check\n- tests/auth/login_spec.sh: add coverage'
local ex1_a="feat(auth): add multi-factor auth with tests"
local messages
messages="$(jq -n \
--arg s "$system" \
--arg u1 "$ex1_u" --arg a1 "$ex1_a" \
--arg u2 "$diff_text" \
'{messages: [
{role:"system", content:$s},
{role:"user", content:$u1},
{role:"assistant", content:$a1},
{role:"user", content:$u2}
]} | .messages')"
ai_chat "$messages"
}
# Usage:
# git diff --staged | summarize_commit
Why it works:
The rubric sets guardrails (type list, length limit).
One example aligns the model to the desired tone and structure.
4) Keep Examples Reusable and Versioned
Store your canonical few‑shot examples in files and compose them into prompts at runtime. This makes updates trivial.
# examples/slugify.examples.txt
# USER:
Holiday Photos 2025!.zip
# ASSISTANT:
holiday-photos-2025.zip
# USER:
README_FINAL (v2).md
# ASSISTANT:
readme-final-v2.md
Then load them:
build_messages_with_file() {
local system="$1"
local query="$2"
local exfile="$3"
# Convert the file into alternating user/assistant messages
local ex_messages
ex_messages="$(awk '
BEGIN{role=""; content=""}
/^# USER:/{if(content!=""){print role ":" content}; role="user"; content=""; next}
/^# ASSISTANT:/{if(content!=""){print role ":" content}; role="assistant"; content=""; next}
{content = (content=="" ? $0 : content ORS $0)}
END{if(content!=""){print role ":" content}}
' "$exfile" | jq -Rs '
split("\n") | map(select(length>0)) |
map(
{role: (split(":")[0]),
content: (.[index(":")+1:] | join(":") | ltrimstr(" "))}
)
')"
jq -n --arg s "$system" --arg u "$query" --argjson ex "$ex_messages" \
'{messages: ([{role:"system", content:$s}] + $ex + [{role:"user", content:$u}]) } | .messages'
}
# Usage:
# msgs=$(build_messages_with_file "$system" "My Input" "examples/slugify.examples.txt")
# ai_chat "$msgs"
Version these files in Git, review diffs, and iterate as your needs change.
5) Test and Iterate Automatically
Create a tiny harness to guard against regressions as you tweak examples.
run_tests() {
local -a inputs=(
"New Project Plan (Draft).pdf"
"hello_world.TXT"
)
local -a expect=(
"new-project-plan-draft.pdf"
"hello-world.txt"
)
local pass=0 fail=0
for i in "${!inputs[@]}"; do
out="$(slugify "${inputs[$i]}")"
if [[ "$out" == "${expect[$i]}" ]]; then
echo "OK ${inputs[$i]} -> $out"
((pass++))
else
echo "FAIL ${inputs[$i]} -> $out (expected ${expect[$i]})"
((fail++))
fi
done
echo "Pass: $pass Fail: $fail"
[[ $fail -eq 0 ]]
}
Run after changing examples to ensure behavior stays consistent.
Practical Tips
Start with 2–3 high‑quality examples; add only when gaps appear.
Keep temperature low (0–0.3) for consistent behavior.
Use clear delimiters and roles; prefer short, concrete phrasing.
Always specify the desired output format (especially JSON).
Log token usage and set sensible rate limits to control costs.
Conclusion and Next Steps
Few‑shot prompting is the fastest way to “program” an LLM from your shell: no training, just good examples. You saw how to:
Craft strong system instructions and minimal examples.
Enforce schemas for reliable parsing with
jq.Reuse and version your examples.
Test behavior automatically.
Your call to action:
Install
curlandjqwith your package manager.Pick a daily task (slugify, classify, summarize) and encode 2–3 examples.
Wrap it in a Bash function and iterate with a small test set.
Small, well‑chosen examples can turn your shell into a fast, flexible AI workbench—go build your first few‑shot tool today.