← retour aux snippets

sponge: écrire en place depuis un pipeline

Modifier un fichier via un pipeline sans l'écraser prématurément (moreutils sponge), y compris avec sudo.


objectif

Éviter de vider un fichier quand on lit et écrit en même temps depuis un pipeline. sponge (moreutils) absorbe toute l’entrée puis écrit le résultat en une seule fois, pratique pour sort, grep, jq, etc., même sur des fichiers système avec sudo.

code minimal

# dédupliquer et trier un fichier en l'écrivant "en place"
sort -u hosts.txt | sponge hosts.txt

utilisation

# reformater un JSON proprement (indentation stable)
jq . config.json | sponge config.json

# filtrer des lignes (supprimer les commentaires et vides)
grep -Ev '^\s*(#|$)' app.env | sponge app.env

# normaliser un fichier .gitignore (trier, dédupliquer, retirer vides)
grep -Ev '^\s*$' .gitignore | sort -u | sponge .gitignore

# appliquer un sed portable sans -i (BSD/GNU diffèrent)
sed 's/DEBUG=true/DEBUG=false/' .env | sponge .env

# mise à jour d'un vhost nginx de data.pm avec privilèges
sudo sh -c 'generate_nginx | sponge /etc/nginx/sites-available/data.pm.conf'
# ou directement si la commande produit la conf:
nginx -T | grep -A3 'server_name data.pm' | sudo sponge /etc/nginx/inspect.conf

# harmoniser des ACL (ex: allowlist) et réécrire
awk '{$1=tolower($1)}1' allowlist.txt | sort -u | sponge allowlist.txt

# nettoyer des logs de dev (garder les 10k dernières lignes)
tail -n 10000 app.log | sponge app.log

variante(s) utile(s)

# réécrire plusieurs fichiers (boucle sûre)
for f in *.md; do
  awk 'NF' "$f" | sponge "$f"
done

# utiliser sponge derrière une commande qui peut échouer (fail-fast)
set -euo pipefail
( jq '.feature_flags |= keys|length' config.json || exit 1 ) | sponge config.json

# fallback sans sponge (portable): tmp + rename (non root)
tmp="$(mktemp)"; jq . config.json > "$tmp" && mv -f "$tmp" config.json

# fallback root (sudo) sans sponge: tmp + mv atomique
tmp="$(mktemp)"; jq . /etc/app.conf > "$tmp" && sudo mv -f "$tmp" /etc/app.conf

# protéger les permissions (réappliquer mode et propriétaire après réécriture)
mode="$(stat -c '%a' app.env 2>/dev/null || stat -f '%Lp' app.env)"
owner="$(stat -c '%u:%g' app.env 2>/dev/null || stat -f '%u:%g' app.env)"
grep -Ev '^\s*(#|$)' app.env | sponge app.env
chown "$owner" app.env 2>/dev/null || true
chmod "$mode" app.env 2>/dev/null || true

notes

  • sponge lit tout le flux avant d’écrire, évitant que cat fichier | ... > fichier vide le fichier en amont.
  • utile quand -i des outils varie selon les plateformes (ex: sed -i BSD vs GNU). Avec sponge, pas de différence.
  • pour écrire des fichiers système, placez sudo du côté écriture: ... | sudo sponge /chemin/fichier.
  • sponge n’assure pas une écriture “atomique” ni la préservation des permissions. Pour un besoin strict, utilisez un flux tmp + mv (voir snippet “écriture atomique de fichier avec fsync”).
  • installez moreutils pour disposer de sponge (Debian/Ubuntu: sudo apt install moreutils, macOS: brew install moreutils).