Skip to content

Pretty print JSON on the command line

Short answer

To pretty print JSON on the command line, use jq: jq . data.json, or pipe into it with curl -s URL | jq .. Without jq, python3 -m json.tool data.json works wherever Python is installed.

Updated · 4 min read

Which command to use

Tool Command Indent Comes with
jq jq . order.json 2 spaces macOS 15 and later
Python python3 -m json.tool order.json 4 spaces Python
Node.js node -p "JSON.stringify(require('./order.json'), null, 2)" your choice Node.js
PowerShell Get-Content order.json -Raw | ConvertFrom-Json | ConvertTo-Json -Depth 100 2 spaces (7.x), wide (5.1) Windows

json_pp < order.json, which comes with Perl, is often installed on macOS and Linux too. It indents with 3 spaces.

Pretty print JSON on the command line with jq

Install it if jq --version finds nothing:

brew install jq
sudo apt-get install jq
sudo dnf install jq
winget install jqlang.jq

These are for macOS (Homebrew), Debian and Ubuntu, Fedora, and Windows. Then pass the file and ., the filter that outputs its input unchanged:

jq . order.json
{
  "order": "B-77",
  "status": "shipped",
  "lines": [
    {
      "sku": "LAMP-3",
      "qty": 2
    },
    {
      "sku": "BULB-9",
      "qty": 6
    }
  ],
  "notes": null
}
jq -S . order.json
jq --indent 4 . order.json
jq --tab . order.json
jq -c . order.json
jq -C . order.json | less -R

-S sorts keys, --indent takes up to 7 spaces, --tab indents with tabs and -c puts everything on one line. jq colors its output in a terminal; -C keeps the color through a pipe for less -R.

Invalid input stops with a position, for example jq: parse error: Expected another key-value pair at line 1, column 29 for a trailing comma.

Python and Node.js

python3 -m json.tool order.json
python3 -m json.tool --indent 2 --sort-keys order.json
node -p "JSON.stringify(JSON.parse(require('fs').readFileSync(0, 'utf8')), null, 2)" < order.json

On Windows, type py or python instead of python3. Python 3.14 and later also accept python -m json. Every flag is in pretty print JSON in Python, and replacers and key sorting in pretty print JSON in JavaScript.

PowerShell

Get-Content shipment.json -Raw | ConvertFrom-Json | ConvertTo-Json -Depth 100

Always pass -Depth. The default is 2, and deeper objects turn into strings. Windows PowerShell 5.1 does this without a warning:

{
    "order":  "B-77",
    "ship":  {
                 "to":  {
                            "city":  "Leeds",
                            "geo":  "@{lat=53.8; lon=-1.55}"
                        }
             }
}

PowerShell 7.1 and later cut it the same way but print a warning. Other differences in Windows PowerShell 5.1:

  • The indentation is wide, as above, and <, >, ' and & are written as \u003c, \u003e, \u0027 and \u0026.
  • Get-Content reads BOM-less UTF-8 as the ANSI code page, so São Paulo becomes São Paulo. Add -Encoding UTF8.

In PowerShell 7, [1] comes back as 1 unless you add -NoEnumerate to ConvertFrom-Json, and ISO timestamps become DateTime values unless you add -DateKind String (7.5 and later).

Pretty print curl output

curl -s https://api.github.com/repos/jqlang/jq | jq .
curl -s https://api.github.com/repos/jqlang/jq | jq '{name, stars: .stargazers_count}'

-s hides curl’s progress meter. The second filter keeps two fields and renames one. In Windows PowerShell 5.1, curl is an alias for Invoke-WebRequest, so type curl.exe, or use Invoke-RestMethod URL | ConvertTo-Json -Depth 100.

Decode a JWT payload with jq

A JWT is three base64url parts separated by dots, and the middle one is the payload:

TOKEN='eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyLTQ4MjEiLCJuYW1lIjoiTWlhIENoZW4iLCJhdWQiOiJodHRwczovL2FwaS5leGFtcGxlLmNvbS8_dj0yIiwicm9sZSI6ImVkaXRvciIsImlhdCI6MTc5MzgwMDAwMCwiZXhwIjoxNzkzODAzNjAwfQ.j37yaZ5oSIjQgIT53XzXhPhVLSR0b_Vkmsl8Pss1HM8'
jq -R 'split(".")[1] | gsub("-"; "+") | gsub("_"; "/") | @base64d | fromjson' <<< "$TOKEN"
{
  "sub": "user-4821",
  "name": "Mia Chen",
  "aud": "https://api.example.com/?v=2",
  "role": "editor",
  "iat": 1793800000,
  "exp": 1793803600
}

The two gsub calls turn base64url into the standard alphabet. Without them, a token containing _ or - fails with jq: error (at <stdin>:1): string ("eyJzdWIiOi...") is not valid base64 data. Missing = padding is fine. Add | .exp | todate to read the expiry as a date. On Windows, where <<< does not exist, save the filter as jwt.jq and the token as token.txt, then run jq -R -f jwt.jq token.txt. This decodes only; it does not verify the signature. The JWT decoder does the same in the browser and shows whether exp has passed.

Windows quoting and encoding

  • cmd has no single quotes. Wrap the filter in double quotes and escape inner ones: jq ".lines[] | select(.sku == \"LAMP-3\")" order.json.
  • Windows PowerShell 5.1 strips double quotes inside arguments, so jq '.lines[] | select(.sku == "LAMP-3")' order.json fails with jq: error: LAMP/0 is not defined. Fix it with \"LAMP-3\", with a filter file (jq -f filter.jq order.json), or with PowerShell 7.3 or later, which passes the quotes through.
  • Piping in Windows PowerShell 5.1 (Get-Content order.json | jq .) sends text as US-ASCII, so accented letters arrive as ?. Pass the file name to jq or Python instead.

To pretty print without a terminal, paste the JSON into JSON Prettier. Editor and language guides are in JSON guides.

Questions

Will pretty printing change my numbers?

It can. Given {"id":12345678901234567890,"price":1.10}, jq 1.8 prints both as written; python3 -m json.tool keeps the id but prints 1.1; the Node one-liner prints the id as 12345678901234567000, because JSON.parse stores numbers as doubles.

How do I pretty print a JSON Lines file?

jq reads one document after another, so jq . events.jsonl formats every line, blank lines included. jq -c . events.jsonl turns pretty output back into one document per line.

Tools for this

Esc