9 min · +150 XP
Claude in scripts with claude -p
Run one task without the interactive window and use the answer in a script.
By the end of this lesson
You will be able to call Claude Code from a script, control what it may do, read its answer as JSON and cap what a run can spend.
With -p (print mode), Claude Code takes one task, does it and prints the answer, without the interactive window. That lets you call it from a shell script, a scheduled job or a build step, like any other command-line tool. It exits with code 0 on success and a non-zero code on failure, so the script can react.
“Write a script explain-build-error.sh that sends build-error.txt to claude -p, asks for the root cause in two sentences and saves the answer to output.txt. Allow only the Read tool, cap the spend at $1, run it once and show me the result.”
Claude writes the script with the right flags and runs it. The commands below are what it produces, so you can read and adjust them.
claude -p "What does the auth module do?"
Run it inside the project folder. Claude reads what it needs, prints the answer and exits.
cat build-error.txt | claude -p 'concisely explain the root cause of this build error' > output.txt
Print mode reads standard input. Piped input is capped at 10MB; for larger files, name the file in the prompt instead of piping it.
Output a script can read
Three output formats
- 1text, the default: plain text.
- 2json: one object with result, session_id, total_cost_usd with a per-model breakdown, and other metadata.
- 3stream-json: one JSON event per line as the work happens. Add --verbose, and --include-partial-messages for text as it is generated.
claude -p "Extract the main function names from auth.py" \
--output-format json \
--json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}' \
| jq '.structured_output'The validated answer is in structured_output. If a run ends without valid structured output, for example because Claude declined, the field is missing, so check for it before using it.
Permissions without a person
In print mode nobody is there to answer a permission question, so decide in advance what Claude may do. --allowedTools lists tools that run without asking, in the same rule syntax as settings. --permission-mode sets the baseline for everything else.
claude -p "Look at my staged changes and create an appropriate commit" \ --allowedTools "Bash(git diff *),Bash(git log *),Bash(git status *),Bash(git commit *)"
The space before * matters. Bash(git diff *) allows any command that starts with git diff; Bash(git diff*) would also match git diff-index.
Choosing the baseline
- 1--permission-mode dontAsk: anything not allowed in advance is refused. Suited to locked-down CI runs.
- 2--permission-mode acceptEdits: file writes and common file commands such as mkdir, mv and cp run; other shell commands still need an allow rule.
- 3--permission-mode auto: a classifier reviews actions in place of you. Add --permission-prompts none so anything that would wait for a person is denied and Claude is told not to retry it.
Scripts that behave the same everywhere
claude --bare -p "Summarize README.md" --allowedTools "Read" --max-budget-usd 1.00
--bare skips the hooks, skills, plugins, MCP servers, auto memory and CLAUDE.md found on the machine, so a teammate's setup cannot change the result. It never reads your subscription login, so set ANTHROPIC_API_KEY. --max-budget-usd stops the run when estimated spend reaches the amount; spend can pass the cap a little, so leave headroom.
session_id=$(claude -p "Start a review" --output-format json | jq -r '.session_id') claude -p "Continue that review" --resume "$session_id"
--continue picks up the most recent conversation in this folder; --resume with an ID picks a particular one, from any project on this machine.
The same agent loop is available as the Agent SDK, with packages for Python and TypeScript; it used to be called the Claude Code SDK. Move to it when a script grows into a program and you want native message objects, callbacks that approve tools, and structured outputs. For most scripts, claude -p is enough.
claude -p skips the workspace trust dialog. Without --bare it runs the hooks in the project's .claude/settings.json and connects the servers in its .mcp.json, even in a folder you have never opened before. Before you script Claude over a repository you did not write, read its .claude/ folder or use --bare.
Your CI job must give the same result on every machine and must not pick up anyone's personal hooks or CLAUDE.md. Which flag?
You pass --json-schema and read .structured_output, but sometimes the field is missing. Why?
Facts checked against the official Claude Code docs October 24, 2026 · source

Author
Evgenii Arsentev
PhD · Chief Executive Officer, digital health