Skip to main content

Scripting with Headless Mode

Module: Automation | Lesson: 2 of 3 | Time: ~10 minutes

What You Will Learn

  • How to write PowerShell scripts that call Claude
  • How to pipe input and output between commands
  • How to process Claude's JSON output in scripts

Prerequisites

Why Script with Claude?

In the previous lesson you learned to run single headless commands. That is useful, but the real power comes when you put multiple headless calls together into a script -- a file that runs a series of commands automatically. Instead of typing each command by hand, you write them once and let PowerShell do the work.

Imagine you have 20 Markdown files and you want Claude to summarize each one. You could run claude -p twenty times manually, or you could write a script that does it all in one go. That is what this lesson is about.


Your First Claude Script

Let's start with the simplest possible script. Open PowerShell and create a practice folder:

mkdir $env:USERPROFILE\claude-scripts
cd $env:USERPROFILE\claude-scripts

Now create a script file. You can do this with Notepad:

notepad summarize-single.ps1

When Notepad opens, paste this content:

# summarize-single.ps1
# A simple script that asks Claude to summarize a file

param(
[string]$FilePath
)

if (-not (Test-Path $FilePath)) {
Write-Host "Error: File not found: $FilePath" -ForegroundColor Red
exit 1
}

$content = Get-Content $FilePath -Raw
$prompt = "Summarize the following text in 2-3 sentences:`n`n$content"

Write-Host "Summarizing: $FilePath" -ForegroundColor Cyan
$response = claude -p $prompt
Write-Host $response

Save the file and close Notepad. Now let's create a test file to summarize:

"PowerShell is a task automation framework from Microsoft. It includes a command-line shell and a scripting language. PowerShell is built on the .NET framework and helps IT professionals control and automate the administration of Windows operating systems." | Out-File -FilePath test-article.txt

Run your script:

pwsh -File summarize-single.ps1 -FilePath test-article.txt

You should see Claude's summary printed in your terminal. You just automated your first Claude task.

info

The param() block at the top of the script defines a parameter. This lets you pass the file path as an argument when running the script, making it reusable for any file.


Piping Input to Claude

PowerShell's pipe (|) operator lets you send the output of one command directly into another. You can pipe content to Claude using the -p flag combined with input from other commands:

# Pipe file contents to Claude
Get-Content test-article.txt | claude -p "Summarize this text in one sentence"
# Pipe command output to Claude
git log --oneline -10 | claude -p "Describe what this project has been working on based on these git commits"
# Pipe error output to Claude for help
Get-ChildItem nonexistent-folder 2>&1 | claude -p "Explain this PowerShell error and how to fix it"

Piping is powerful because it lets you chain Claude into existing workflows without saving intermediate files.


Processing JSON Output in Scripts

For scripts that need to do something with Claude's response (not just display it), use JSON output and parse it with PowerShell:

# Get JSON response and parse it
$jsonResponse = claude -p "What are the 4 seasons?" --output-format json
$parsed = $jsonResponse | ConvertFrom-Json

# Access individual fields
Write-Host "Answer: $($parsed.result)"
Write-Host "Cost: $($parsed.cost_usd) USD"
Write-Host "Duration: $($parsed.duration_ms) ms"

The ConvertFrom-Json cmdlet turns the JSON string into a PowerShell object, so you can access fields like result, cost_usd, and is_error using dot notation.

tip

Always check the is_error field in production scripts. If something goes wrong, is_error will be true and you can handle the failure gracefully instead of crashing.


Batch Processing Multiple Files

Here is the real payoff: processing many files in a loop. Create a script called batch-summarize.ps1:

# batch-summarize.ps1
# Summarize all .md files in a folder and save results

param(
[string]$FolderPath = ".",
[string]$OutputFile = "summaries.md"
)

# Find all Markdown files
$files = Get-ChildItem -Path $FolderPath -Filter "*.md" -File

if ($files.Count -eq 0) {
Write-Host "No .md files found in $FolderPath" -ForegroundColor Yellow
exit 0
}

Write-Host "Found $($files.Count) Markdown file(s) to summarize." -ForegroundColor Cyan
Write-Host ""

# Start building the output
$output = "# File Summaries`n`n"
$output += "Generated on $(Get-Date -Format 'yyyy-MM-dd HH:mm')`n`n"

$totalCost = 0

foreach ($file in $files) {
Write-Host "Processing: $($file.Name)..." -ForegroundColor Green

$content = Get-Content $file.FullName -Raw
$prompt = "Summarize this Markdown document in 2-3 sentences. Be concise:`n`n$content"

# Call Claude in headless mode with JSON output
$jsonResponse = claude -p $prompt --output-format json
$parsed = $jsonResponse | ConvertFrom-Json

if ($parsed.is_error) {
Write-Host " Error processing $($file.Name), skipping." -ForegroundColor Red
continue
}

# Add to output
$output += "## $($file.Name)`n`n"
$output += "$($parsed.result)`n`n"
$output += "---`n`n"

$totalCost += $parsed.cost_usd
Write-Host " Done. Cost: `$$($parsed.cost_usd)" -ForegroundColor Gray
}

# Save the output file
$output | Out-File -FilePath $OutputFile -Encoding utf8

Write-Host ""
Write-Host "All summaries saved to $OutputFile" -ForegroundColor Cyan
Write-Host "Total cost: `$$([math]::Round($totalCost, 4))" -ForegroundColor Cyan

To test this, create a few sample Markdown files:

"# Git Basics`nGit is a version control system that tracks changes to files. It lets multiple people collaborate on code." | Out-File sample1.md
"# PowerShell Tips`nPowerShell uses cmdlets like Get-ChildItem and Set-Location. Pipelines connect commands together." | Out-File sample2.md
"# Claude Code`nClaude Code is an AI coding assistant that runs in your terminal. It can read, write, and edit files." | Out-File sample3.md

Now run the batch script:

pwsh -File batch-summarize.ps1 -FolderPath "." -OutputFile "summaries.md"

When it finishes, open summaries.md to see all three summaries in one document.

warning

Each call to claude -p costs tokens. When batch processing many files, costs can add up. Always test with a small number of files first, and check the total cost printed by the script. Module 13 covers cost management in detail.


Error Handling in Scripts

Production scripts should handle errors gracefully. Here is a pattern you can reuse:

function Invoke-Claude {
param(
[string]$Prompt,
[int]$MaxRetries = 2
)

for ($attempt = 1; $attempt -le $MaxRetries; $attempt++) {
try {
$jsonResponse = claude -p $Prompt --output-format json
$parsed = $jsonResponse | ConvertFrom-Json

if (-not $parsed.is_error) {
return $parsed
}

Write-Host " Attempt $attempt failed (Claude error). Retrying..." -ForegroundColor Yellow
}
catch {
Write-Host " Attempt $attempt failed (exception): $_" -ForegroundColor Yellow
}

Start-Sleep -Seconds 2
}

Write-Host " All $MaxRetries attempts failed." -ForegroundColor Red
return $null
}

# Usage:
$result = Invoke-Claude -Prompt "Explain what a function is in programming"
if ($null -ne $result) {
Write-Host $result.result
} else {
Write-Host "Could not get a response from Claude."
}

This function retries up to two times if something goes wrong, with a short pause between attempts.


Try It Yourself

  1. Create the batch summarize script. Follow the instructions above to create batch-summarize.ps1 and test it with the three sample files.

  2. Pipe experiment. Try piping different things into Claude:

    # Pipe your PowerShell version info
    $PSVersionTable | Out-String | claude -p "What version of PowerShell is this and what platform is it running on?"
  3. Add a new feature. Modify batch-summarize.ps1 to also count the number of words in each file and include that in the output. Hint: use ($content -split '\s+').Count to count words.

  4. Error handling. Deliberately pass a bad file path to summarize-single.ps1 and confirm that the error message appears instead of a crash.


What You Learned

  • PowerShell scripts can call claude -p to automate tasks without manual interaction
  • The pipe operator (|) sends output from one command as input to Claude
  • ConvertFrom-Json parses Claude's JSON output into PowerShell objects you can work with
  • Batch processing loops over multiple files, calling Claude for each one
  • Error handling with retries makes scripts robust enough for real-world use
  • Always monitor costs when running batch operations

Help Us Improve

How was this lesson? Take 2 minutes to share your feedback — it helps us make the tutorials better for everyone.

Give Feedback →

Next Up

Next: CI/CD Integration