← All one-liners·#082·ops·self contained·power

Dedupe a JSON file in place, safely

jq can't write to the file it just read — sponge (from moreutils) buffers the whole pipeline in memory before opening the destination, so any in-place edit (dedupe, reformat, prune) collapses to one line instead of a temp-file-and-mv dance.

Setup
  • → apt install moreutils # provides sponge, ts, pee alongside it
Cost per run
free
The one-liner
$ jq 'sort_by(.host) | unique_by(.host)' hosts.json | sponge hosts.json
What each stage does
  1. [01] bashecho '[{"host":"api.example.com","status":"up"},{"host":"db.example.com","status…
    Stand-in for any local JSON state that accumulates duplicates over time — a host inventory, a dedupe cache, a queue snapshot. 5 rows, 2 hosts repeated.
  2. [02] jqjq 'sort_by(.host) | unique_by(.host)' hosts.json
    Read hosts.json, sort by host, then keep only the first row per host. Output goes to stdout — hosts.json itself is still untouched at this point.
  3. [03] spongesponge hosts.json
    Soaks up all of stdin into memory before it opens hosts.json for writing. `jq ... hosts.json > hosts.json` would truncate the file to empty the instant the shell's redirection opens it — sponge removes that race entirely.
  4. [04] catcat hosts.json
    Confirms the rewrite landed: 5 rows in, 3 unique rows out, same path, no temp file left behind.
Expected output (sample)
[
  {
    "host": "api.example.com",
    "status": "up"
  },
  {
    "host": "cache.example.com",
    "status": "up"
  },
  {
    "host": "db.example.com",
    "status": "down"
  }
]
Caveats & tips
  • Verified on moreutils 0.69 (Ubuntu 24.04 apt package); sponge takes no --version flag, that's normal.
  • sponge buffers entirely in memory — great for KB/MB config, cache, and state files; not the tool for multi-GB pipelines.
  • If the upstream command crashes, sponge writes nothing at all — the file is never left half-truncated, which is the whole point.
  • The general shape is `<command> file | sponge file` any time you'd otherwise reach for `cmd file > tmp && mv tmp file` — works with sed, yq, csvsort, anything that reads a file and prints a new version of it.