Posted on
Artificial Intelligence

Chain-of-Thought Prompting

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

Chain-of-Thought Prompting for Bash Users: Turn Vague Ideas into Reliable Shell Commands

If you’ve ever stared at a terminal wondering how to translate a fuzzy requirement—“clean old logs safely,” “rename files by pattern,” “mirror a directory with filters”—into a correct, idempotent Bash command, you’re not alone. LLMs can help, but naively asking “give me a command” often yields brittle, unsafe outputs.

Enter Chain-of-Thought prompting: a technique for steering models to plan before answering so you get safer, more accurate results. In this post, you’ll learn how to apply the core principles from Chain-of-Thought—without demanding verbose rationales—to generate better Bash one-liners, pipelines, and scripts.

You’ll get:

  • Why Chain-of-Thought prompting matters for shell work

  • 3–5 actionable prompt patterns you can use today

  • Real-world examples you can run from Linux

  • Minimal CLI setup using curl and jq (apt, dnf, and zypper install lines included)

Note: We’ll encourage the model to think through the problem internally but only return final, concise outputs. That keeps results focused and easier to automate.


Why Chain-of-Thought Prompting Works (Especially for Bash)

  • Shell tasks are compositional: you combine tools like find, xargs, sed, awk, grep, tar. A tiny slip (glob, quoting, locale, whitespace) can cascade into data loss or silent errors.

  • Planning reduces errors: by nudging the model to internally decompose the task (inputs, constraints, edge cases), you get safer defaults (e.g., dry-run first, null-delimited paths, quoting).

  • Structure beats prose: well-scoped prompts and structured outputs (e.g., JSON fields) make it easy to review and automate.

You don’t need the model to show every thought; you want it to think carefully, then return a precise, auditable answer.


Prerequisites (command-line friendly)

We’ll use curl to call an LLM API and jq to parse JSON. Install them with your package manager:

  • 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 install -y curl jq

Set your API key (example uses OpenAI-compatible endpoints). Replace with your provider and key as needed:

export OPENAI_API_KEY="sk-REPLACE_ME"
export OPENAI_API_BASE="https://api.openai.com/v1"
export OPENAI_MODEL="gpt-4o-mini"

Helper function to send prompts:

ai() {
  local system_msg="$1"
  local user_msg="$2"
  curl -s "${OPENAI_API_BASE}/chat/completions" \
    -H "Authorization: Bearer ${OPENAI_API_KEY}" \
    -H "Content-Type: application/json" \
    -d "$(jq -n \
      --arg sm "$system_msg" \
      --arg um "$user_msg" \
      --arg m  "${OPENAI_MODEL:-gpt-4o-mini}" \
      '{
        model: $m,
        temperature: 0.2,
        messages: [
          {role:"system", content:$sm},
          {role:"user",   content:$um}
        ]
      }')" \
    | jq -r '.choices[0].message.content'
}

Tip: Set temperature low for precise shell outputs.


Core Patterns You Can Use Today

1) Final-Only Answer with Constraints

Tell the model to plan internally but only return the final command plus a terse summary. Also set guardrails (POSIX, null-delimited paths, dry-run).

Prompt template:

SYSTEM:
You are a senior Bash engineer. Think through the task privately.
Return only:
1) final_command (a single safe Bash command)
2) summary (one sentence)
Constraints:

- Prefer POSIX tools where possible

- Use null-delimited paths when traversing files

- Quote variables; handle spaces/newlines safely

- Default to dry-run when deleting or moving

- If ambiguous, ask a short clarifying question instead of guessing

USER:
Task: <describe your task>
Environment: Linux (bash 5), GNU coreutils, locale C.UTF-8
Example input shape: <optional sample>
Output format (JSON):
{"final_command": "...", "summary": "..."}

Example (compress logs older than 14 days, keep permissions):

ai \
"$(cat <<'EOF'
You are a senior Bash engineer. Think through the task privately.
Return only:
1) final_command
2) summary
Constraints:

- Prefer POSIX tools where possible

- Use null-delimited paths when traversing files

- Quote variables; handle spaces/newlines safely

- Default to dry-run when deleting or moving

- If ambiguous, ask a short clarifying question instead of guessing
EOF
)" \
"$(cat <<'EOF'
Task: Compress *.log files older than 14 days under /var/log/myapp without touching already .gz files. Preserve ownership/mode. Show a dry-run first.
Environment: Linux (bash 5), GNU coreutils, locale C.UTF-8
Output format (JSON):
{"final_command": "...", "summary": "..."}
EOF
)"

You’ll get a JSON blob you can inspect before running.


2) Plan-Then-Answer (Concise), With Safe Defaults

Ask for a short plan and the final command. This yields quick review points (not a long essay).

Prompt template:

SYSTEM:
Plan internally; return a short plan (3 bullets max) and a final command.
Keep both concise. Use safe defaults (dry-run/null-delimited/quoting).

USER:
Goal: <task>
Return:
plan:

- ...

- ...

- ...
final_command: <one-liner>

Example (rename files by pattern safely):

ai \
"Plan internally; return a short plan (3 bullets max) and a final command. Keep both concise. Use safe defaults (dry-run/null-delimited/quoting)." \
"Goal: Rename files *.jpeg to *.jpg under ~/Pictures recursively, no overwrites, print changes (dry-run). Return:
plan:

- ...

- ...

- ...
final_command: ..."

3) Structured JSON for Automation

Force the model to output machine-readable fields your Bash can parse and act on.

Prompt template:

SYSTEM:
Return JSON only with keys: action, final_command, risks, test_cmd.

- final_command must be safe and idempotent

- Include a test_cmd that previews effects without changes

USER:
Task: <task>
Environment: Linux bash 5
JSON schema:
{"action": "...", "final_command": "...", "risks": ["..."], "test_cmd": "..."}

Example and parse:

resp="$(ai \
"Return JSON only with keys: action, final_command, risks, test_cmd. - final_command must be safe and idempotent - Include a test_cmd that previews effects without changes" \
"Task: Delete thumbnail cache files older than 30 days under ~/.cache/thumbnails. Environment: Linux bash 5
JSON schema:
{\"action\":\"...\",\"final_command\":\"...\",\"risks\":[\"...\"],\"test_cmd\":\"...\"}")"

echo "$resp" | jq .
echo "Test dry-run command:"
echo "$resp" | jq -r '.test_cmd'

# After review, run the final command:
# eval "$(echo "$resp" | jq -r '.final_command')"

Always review before running.


4) Self-Check Loop (Two-Pass)

Generate a draft, then ask the model to critique it for edge cases, and finally refine.

Draft:

draft="$(ai \
"You are a careful Bash engineer. Think privately; return only 'final_command' and 'summary' as JSON." \
"Task: Move .mp4 files larger than 500MB from ~/Downloads to /mnt/media/videos, keeping directory structure; if not mounted, do nothing; dry-run first. JSON keys: final_command, summary")"
echo "$draft" | jq .

Critique and refine:

refined="$(ai \
"Critique the provided command for safety, then return an improved version as JSON with keys: issues (array), final_command, summary. Keep issues concise." \
"Here is the previous result:
$draft")"
echo "$refined" | jq .

Use the refined command after reviewing issues.


5) Clarify Ambiguities Instead of Guessing

Encourage the model to ask one short clarifying question when the task is underspecified. This prevents wrong assumptions (e.g., wrong extensions, timezone confusion).

Prompt add-on:

If critical details are missing, return:
{"question": "<short question>"} and stop.

Your script can detect .question in the JSON and prompt you interactively.


Real-World Demos

A) Safer delete with dry-run and null delimiters

ai \
"You are a senior Bash engineer. Think privately; return JSON with final_command and summary only. Use find -print0 and xargs -0; default to dry-run." \
"Task: Remove empty directories under ~/work/tmp excluding .git directories. JSON: {\"final_command\":\"...\",\"summary\":\"...\"}"

Look for:

  • find ... -type d -empty -print0

  • xargs -0 -I{} rmdir -v -- "{}" or a dry-run echo first

  • Pruning .git with -name .git -prune -o ...

B) Rsync mirror with safety rails

ai \
"You are a senior Bash engineer. Think privately; return JSON with final_command and summary only. Use rsync with itemize-changes, dry-run, checksum when appropriate." \
"Task: Mirror ~/data to /mnt/backup/data excluding node_modules and *.tmp; preserve perms/owner/times; show what would change first. JSON: {\"final_command\":\"...\",\"summary\":\"...\"}"

Expect safe flags (e.g., -aHAX, --delete, --dry-run, --info=..., --exclude entries) and quoting.


Tips for Better Results

  • Be explicit:

    • OS, shell version, file patterns, size/time criteria, encoding/locale, mountpoints.
  • Prefer dry-run first; require null-delimited traversal; force quoting.

  • Keep temperature low for deterministic commands.

  • Use structured outputs and jq to gate execution.

  • Save proven prompt templates in files and reuse them.


Conclusion and Next Steps

Done right, Chain-of-Thought prompting helps LLMs plan before answering so you get safer, more reliable Bash. You don’t need verbose reasoning in your terminal—just well-structured, auditable results.

Your next steps:

  • Install curl and jq (apt/dnf/zypper lines above).

  • Drop the ai() helper into your shell profile.

  • Start with Pattern 1 (Final-Only with Constraints) for your next tricky one-liner.

  • Evolve to Patterns 3–4 (JSON + self-check) when you want automation.

Have a favorite prompt pattern or a tough shell task you want to “upgrade”? Try the templates here and share your before/after results.