CloudCodeTree LogoCloudCodeTree
AI NewsTutorialsAbout
CloudCodeTree Logo
CloudCodeTree
  • AI News
  • Tutorials
  • About
← Back to AI News
Drive Claude from Shell Scripts: claude -p, Piped Input, and --output-format json

Drive Claude from Shell Scripts: claude -p, Piped Input, and --output-format json

Chris Harper

3 min read

Jun 30, 2026 · 12:13 UTC

AI
Workflow
Claude Code
Best Practices

TL;DR: claude -p "task" exits after one turn; add --output-format json and stdout becomes a machine-readable envelope with the result, token counts, and session ID — the building block for Claude in CI pipelines and scripts.

Claude Code's interactive session is powerful for exploratory work, but CI pipelines, pre-commit hooks, and shell scripts need a different pattern: input in, result out, process exits. claude -p (print mode) is that pattern.

The basics

# Ask Claude about piped content and exit
git diff HEAD~1 | claude -p "Summarize the breaking changes in this diff"

# Process a specific file
claude -p "Does this code have any security issues?" < src/auth.ts

# Combine command output with a prompt
npm test 2>&1 | claude -p "Explain these failures and suggest fixes for each"

-p runs one turn and exits with code 0 on success, non-zero on error.

--output-format json for scripting

By default, claude -p writes the model's response to stdout as plain text. Add --output-format json and you get a structured envelope:

result=$(claude -p "List the three most critical TODOs" \
  --output-format json < README.md)

echo "$result" | jq -r '.result'              # model's full response text
echo "$result" | jq '.usage.input_tokens'     # token usage
echo "$result" | jq -r '.session_id'          # for logging / correlation

The JSON envelope fields: result (response text), usage (input_tokens, output_tokens), session_id.

--output-format stream-json for long outputs

For tasks that produce large or time-sensitive output, stream-json emits one JSON object per line as the model generates:

claude -p "Audit every exported function for missing docs" \
  --output-format stream-json \
  2>/dev/null \
  | while IFS= read -r line; do
      type=$(echo "$line" | jq -r '.type // empty')
      if [ "$type" = "text" ]; then
        echo "$line" | jq -r '.text'
      fi
    done

Each line carries a type field: text, tool_use, tool_result, usage. Redirect stderr to suppress the interactive spinner.

CI pattern: pass/fail from Claude's output

# .github/workflows/security-check.yml
- name: AI security review
  run: |
    git diff origin/main...HEAD \
      | claude -p "Review for security issues. Last line must be PASS or FAIL." \
        --output-format json \
      | jq -r '.result' \
      | tail -1 \
      | grep -q "^PASS"

Claude's exit code reflects process success, not Claude's verdict — parse the output to drive your CI gate.

Restrict tools for read-only analysis

By default, claude -p can use all tools. Limit it for safer CI runs:

claude -p "Review this file for logic errors" \
  --allowedTools "Read,Grep,Glob" \
  --output-format json \
  < src/service.ts

--allowedTools is a comma-separated list. A read-only set (Read,Grep,Glob) gives Claude full text analysis with no filesystem writes, no shell execution, no network.

Sources: