Keine Werbung mögen? Gehen Werbefrei Heute

jq One-Liners Filter and Transform JSON from the CLI Without Writing a Script

Aktualisiert am

Seven practical jq recipes for backend devs and DevOps — covering select, map, to_entries, del, group_by, and real-world API reshaping. Input and output shown for every pattern.

jq One-Liners: Filter and Transform JSON from the CLI Without Writing a Script 1
ANZEIGE Entfernen?

You’re looking at a minified API response in your terminal. You need to extract 3 fields, filter out the nulls, and pipe the result to something else. Writing a Python script for this is 20 lines and 2 minutes you don’t have. jq handles it in one command — if you know the patterns.

Seven recipes below. Each one covers a pattern you’ll hit repeatedly when working with API responses or log files. Input, command, and output shown for every recipe.

Install

brew install jq        # macOS
apt-get install jq     # Ubuntu/Debian
apk add jq             # Alpine (Docker images)

Recipe 1: Extract a Field from Every Object in an Array

The most common thing you’ll do with jq: pull one field from every item in a JSON array.

Input (users.json)

[
  {"id": 1, "name": "Alice", "email": "alice@example.com", "role": "admin"},
  {"id": 2, "name": "Bob",   "email": "bob@example.com",   "role": "user"},
  {"id": 3, "name": "Carol", "email": "carol@example.com", "role": "user"}
]
jq '.[].name' users.json
# → "Alice"
#   "Bob"
#   "Carol"  (newline-separated stream)

.[] iterates over every element in the array; .name plucks the field. The output is a stream — useful for piping to other commands. If you need a proper JSON array back:

jq 'map(.name)' users.json
# → ["Alice", "Bob", "Carol"]

map(.name) ist eine Abkürzung für [.[] | .name] — it applies the expression to each item and wraps the results in an array. You’ll use map() a lot.

Recipe 2: Filter with select

Keep only the objects that match a condition; drop the rest.

jq 'map(select(.role == "admin"))' users.json
[
  {"id": 1, "name": "Alice", "email": "alice@example.com", "role": "admin"}
]

select(expr) passes the item through only when the expression is truthy — everything else disappears. Wrapping it in map() keeps the output as an array. More examples:

# Numeric comparison
jq 'map(select(.score > 80))' results.json

# Not-null check
jq 'map(select(.error != null))' events.json

# String contains (requires test)
jq 'map(select(.message | test("timeout")))' logs.json

Recipe 3: Reshape Objects with map

Pick only the fields you need and optionally rename them in the same pass.

jq 'map({name: .name, role: .role})' users.json
[
  {"name": "Alice", "role": "admin"},
  {"name": "Bob",   "role": "user"},
  {"name": "Carol", "role": "user"}
]

You’re building a new object literal for each item. To rename a key, change the left side of the colon:

jq 'map({username: .name, access_level: .role})' users.json

One shorthand that saves keystrokes: {name} is equivalent to {name: .name}. If you want a field as-is, you don’t have to write the key twice:

jq 'map({id, name, role})' users.json

Recipe 4: Rename Keys with to_entries und from_entries

map is good when you know which keys exist. When you need to transform the keys themselves — add a prefix, convert naming conventions, or rename based on a lookup — to_entries is the right pattern.

Input (config.json)

{"api_url": "https://api.example.com", "timeout": 30, "retry_count": 3}
# Add cfg_ prefix to every key
jq 'to_entries | map(.key = "cfg_" + .key) | from_entries' config.json
{"cfg_api_url": "https://api.example.com", "cfg_timeout": 30, "cfg_retry_count": 3}

to_entries turns {"k": "v"} hinein [{"key": "k", "value": "v"}]. After you mutate the entries in the resulting array, from_entries reconstructs the object. To rename one specific key without touching the others:

jq 'to_entries | map(if .key == "api_url" then .key = "endpoint" else . end) | from_entries' config.json

Recipe 5: Strip Fields with del

The inverse of reshaping: keep everything and remove a few specific fields. The main use case is stripping sensitive data before logging or before passing a payload to a third-party service.

jq 'map(del(.email))' users.json
[
  {"id": 1, "name": "Alice", "role": "admin"},
  {"id": 2, "name": "Bob",   "role": "user"},
  {"id": 3, "name": "Carol", "role": "user"}
]

Delete multiple fields at once, or delete nested paths:

# Multiple top-level fields at once
jq 'del(.password, .token, .refresh_token)' user.json

# Nested path
jq 'del(.user.internal_id)' response.json

# All fields named "debug" at any depth (recursive descent)
jq 'del(.. | .debug? // empty)' response.json

Recipe 6: Filter, Reshape, and Sort in One Pass

The real payoff is chaining these primitives. Here’s a pattern that shows up constantly when working with paginated API responses: descend into a nested array, filter it, reshape the objects, and sort the result.

Input (repos.json)

{
  "total_count": 3,
  "items": [
    {"id": 1, "name": "repo-alpha", "stargazers_count": 142, "language": "Go",     "private": false},
    {"id": 2, "name": "repo-beta",  "stargazers_count": 89,  "language": "Python", "private": true},
    {"id": 3, "name": "repo-gamma", "stargazers_count": 310, "language": "Go",     "private": false}
  ]
}
jq '.items
  | map(select(.private == false and .language == "Go"))
  | sort_by(-.stargazers_count)
  | map({name, stars: .stargazers_count})' repos.json
[
  {"name": "repo-gamma", "stars": 310},
  {"name": "repo-alpha", "stars": 142}
]

The pipeline:

  • .items — descend into the nested array
  • map(select(...)) — filter in a single pass; and chains conditions
  • sort_by(-.stargazers_count) — negate the value for descending sort; sort_by(.field) alone gives ascending
  • map({name, stars: .stargazers_count}) — final reshape; {name} ist eine Abkürzung für {name: .name}

Recipe 7: Count Log Levels with group_by

Frequency analysis on structured log output — no database needed, no awk gymnastics.

Input (logs.json)

[
  {"level": "error", "msg": "connection timeout",     "service": "auth"},
  {"level": "info",  "msg": "request received",       "service": "api"},
  {"level": "error", "msg": "null pointer exception", "service": "worker"},
  {"level": "warn",  "msg": "slow query detected",    "service": "db"},
  {"level": "error", "msg": "rate limit exceeded",    "service": "api"}
]
jq 'group_by(.level) | map({level: .[0].level, count: length}) | sort_by(-.count)' logs.json
[
  {"level": "error", "count": 3},
  {"level": "info",  "count": 1},
  {"level": "warn",  "count": 1}
]

group_by(.level) returns an array of arrays — each sub-array holds all entries that share the same level value. .[0].level grabs the level name from the first item in the group; length counts how many entries are in the group.

Add a per-service breakdown in the same query:

jq 'group_by(.level) | map({
  level:    .[0].level,
  count:    length,
  services: map(.service) | unique
})' logs.json

Schnellübersicht

The patterns above in table form for when you need a quick reminder:

What you wantjq expression
All values of a fieldmap(.field)
Filter matching itemsmap(select(.field == "val"))
Reshape each objectmap({newKey: .oldKey})
Rename all keysto_entries | map(.key = ...) | from_entries
Remove fieldsmap(del(.field1, .field2))
Sort ascendingsort_by(.field)
Sort descendingsort_by(-.field)
Count by groupgroup_by(.field) | map({key: .[0].field, count: length})
Get all keys of an objectkeys
Count items in arraylength
Unique values of a fieldmap(.field) | unique

Before You Start: Tame the Blob

API responses often come back minified or deeply nested. If you’re not sure of the path you need, paste the JSON into the JSON-Formatierer — it pretty-prints with collapsible nodes so you can spot the path before writing the jq expression. After running a transformation, the JSON-Vergleich tool is useful for verifying the before/after diff when you’re reshaping or stripping fields and want to confirm nothing unexpected changed.

These seven patterns cover the bulk of JSON wrangling you’ll do at the command line. The real power is chaining them — once you can filter, reshape, and sort, you can process most API responses without touching a script file.

Möchten Sie werbefrei genießen? Werde noch heute werbefrei

Erweiterungen installieren

IO-Tools zu Ihrem Lieblingsbrowser hinzufügen für sofortigen Zugriff und schnellere Suche

Zu Chrome-Erweiterung Zu Kantenerweiterung Zu Firefox-Erweiterung Zu Opera-Erweiterung

Die Anzeigetafel ist eingetroffen!

Anzeigetafel ist eine unterhaltsame Möglichkeit, Ihre Spiele zu verfolgen. Alle Daten werden in Ihrem Browser gespeichert. Weitere Funktionen folgen in Kürze!

ANZEIGE Entfernen?
ANZEIGE Entfernen?
ANZEIGE Entfernen?

Nachrichtenecke mit technischen Highlights

Beteiligen Sie sich

Helfen Sie uns, weiterhin wertvolle kostenlose Tools bereitzustellen

Kauf mir einen Kaffee
ANZEIGE Entfernen?