GitHub CLI is designed for both interactive use and shell scripting. This chapter covers output formatting, exit codes, and automation patterns.
Output Formats
By default, gh prints human-readable plain text. For machine-readable output, use --json, --jq, and --template.
JSON Output (--json)
Most gh commands support --json to output structured data.
# Specify which fields you want
gh pr list --json number,title,author
# Discover available fields (omit the field list)
gh pr list --json
# Error: available fields: additions, assignees, author, ...Output is a JSON array or object that can be piped to jq, processed in scripts, or formatted with --jq and --template.
Filtering with --jq
The --jq flag applies jq expressions to JSON output. You do not need jq installed — it is built into gh.
Basic Extraction
# Get just the titles
gh pr list --json title --jq '.[].title'
# Get number and title
gh pr list --json number,title --jq '.[] | "#\(.number) \(.title)"'
# Filter by condition
gh issue list --json number,title,labels --jq '[.[] | select(.labels | length > 0)]'
# Count results
gh issue list --json number --jq 'length'Tab-Separated Values
gh pr list --json number,title,author --jq '.[] | [.number, .title, .author.login] | @tsv'CSV Output
gh pr list --json number,title --jq '.[] | [.number, .title] | @csv'Complex Filtering
# Issues with a specific label
gh issue list --json number,title,labels \
--jq '[.[] | select(.labels[].name == "bug")] | .[] | "#\(.number) \(.title)"'
# PRs by a specific author
gh pr list --json number,title,author \
--jq '[.[] | select(.author.login == "octocat")]'Go Templates (--template)
The --template flag uses Go's text/template syntax for custom formatting.
Basic Usage
gh pr list --json number,title --template '{{range .}}#{{.number}} {{.title}}
{{end}}'Built-in Template Functions
| Function | Description | Example |
|---|---|---|
autocolor <style> <text> | Colors output only in terminals | {{.title | autocolor "green"}} |
color <style> <text> | Always applies ANSI color | {{color "red" .title}} |
join <sep> <list> | Joins list with separator | {{join ", " .labels}} |
pluck <field> <list> | Extracts a field from each item | {{pluck "name" .labels}} |
tablerow <fields>... | Aligned table row | {{tablerow .number .title}} |
tablerender | Renders accumulated table rows | {{tablerender}} |
timeago <time> | Relative time ("2 hours ago") | {{timeago .createdAt}} |
timefmt <fmt> <time> | Formatted timestamp | {{timefmt "2006-01-02" .createdAt}} |
truncate <n> <text> | Truncates to n characters | {{truncate 40 .title}} |
hyperlink <url> <text> | Terminal hyperlink | {{hyperlink .url .title}} |
Table Output
gh pr list --json number,title,author,updatedAt --template \
'{{range .}}{{tablerow (printf "#%v" .number | autocolor "green") .title .author.login (timeago .updatedAt)}}{{end}}{{tablerender}}'Conditional Output
gh pr list --json number,title,isDraft --template \
'{{range .}}{{if .isDraft}}[DRAFT] {{end}}#{{.number}} {{.title}}
{{end}}'String Functions (from Sprig)
Available helpers: contains, hasPrefix, hasSuffix, regexMatch.
gh issue list --json number,title --template \
'{{range .}}{{if contains "bug" .title}}#{{.number}} {{.title}}
{{end}}{{end}}'Exit Codes
| Code | Meaning |
|---|---|
0 | Command completed successfully |
1 | Command failed |
2 | Command was cancelled |
4 | Authentication required |
Specific commands may define additional exit codes. Check each command's documentation.
Using Exit Codes in Scripts
if gh pr checks --fail-fast; then
echo "All checks passed"
gh pr merge --squash --delete-branch
else
echo "Checks failed"
exit 1
fiScripting Patterns
Disable Interactive Prompts
export GH_PROMPT_DISABLED=1
# Or pass --yes where availableForce TTY-Style Output in Pipes
export GH_FORCE_TTY=1
gh pr list | cat # Still shows colored, formatted outputIterate Over Results
# Process each issue number
gh issue list --json number --jq '.[].number' | while read -r num; do
echo "Processing issue #$num"
gh issue view "$num" --json title --jq '.title'
doneConditional Workflows
# Create a release only if there are changes since the last tag
if gh api repos/{owner}/{repo}/compare/v1.0.0...HEAD --jq '.ahead_by' | grep -qv '^0$'; then
gh release create v1.1.0 --generate-notes
fiParallel Execution
# Close all issues with a specific label
gh issue list --label "wontfix" --json number --jq '.[].number' | \
xargs -P 4 -I {} gh issue close {} --reason "not planned"Working with JSON in Scripts
# Store JSON output in a variable
PR_DATA=$(gh pr view 123 --json title,state,mergeable)
# Extract fields
TITLE=$(echo "$PR_DATA" | jq -r '.title')
STATE=$(echo "$PR_DATA" | jq -r '.state')
echo "PR '$TITLE' is $STATE"Disabling Colors
# Any of these work
NO_COLOR=1 gh pr list
CLICOLOR=0 gh pr list
gh pr list | cat # Automatically detected as non-TTYExercises
- List PRs as JSON:
gh pr list --json number,title,author - Extract just titles:
gh pr list --json title --jq '.[].title' - Build a table:
gh pr list --json number,title --template '{{range .}}{{tablerow .number .title}}{{end}}{{tablerender}}' - Use exit codes:
gh pr checks && echo "Pass" || echo "Fail" - Iterate over issues:
gh issue list --json number --jq '.[].number' | head -3