← retour aux snippets

xargs: paralléliser en sécurité

Exécuter des tâches en parallèle (P jobs), avec entrées sûres (-0), limites et codes de retour fiables.


objectif

Lancer efficacement des commandes en parallèle sans casser sur les espaces/newlines, contrôler le niveau de parallélisme et échouer proprement si un job tombe en erreur.

code minimal

# traiter des fichiers en parallèle (4 jobs), noms sûrs via NUL
find . -type f -name "*.log" -print0 | xargs -0 -n1 -P4 gzip -n

utilisation

# 1) paralléliser un script sur une liste d'URLs (idempotent, 8 jobs)
cat urls.txt \
  | sed -n 's/^\s*\(https\?:\/\/[^ ]\+\)\s*$/\1/p' \
  | xargs -r -n1 -P8 -I{} sh -c 'set -Eeuo pipefail; curl -fsS -m 15 "{}" -o /dev/null'

# 2) dynamiquement: P = nb de CPU (Linux/macOS)
P="$( (command -v nproc >/dev/null && nproc) || sysctl -n hw.ncpu )"
find ./images -type f -name "*.png" -print0 \
  | xargs -0 -n1 -P"$P" -I{} sh -c 'set -Eeuo pipefail; optipng -quiet "{}"'

# 3) pipeline avec entrée vide possible: ne rien lancer si vide (-r)
git ls-files "*.ts" -z | xargs -0 -r -n25 -P"$P" eslint --max-warnings=0

# 4) caper les ressources: temps max par job et priorité basse (Linux)
cut -d, -f1 hosts.csv \
  | xargs -r -n1 -P6 -I{} bash -lc 'timeout 10s nice -n 10 curl -fsS "https://api.data.pm/health?host={}" -o /dev/null'

variante(s) utile(s)

# préserver l'ordre de sortie (GNU xargs -r -n1 -P) via un buffer
# (xargs n'offre pas --keep-order; utilisez un identifiant et triez ensuite)
seq 1 20 \
  | xargs -n1 -P8 -I{} sh -c 'i={}; sleep $((RANDOM%3)); echo "$i done"' \
  | sort -n -k1,1

# traiter par lot (batch) pour réduire le coût d'initialisation
git ls-files "*.json" -z | xargs -0 -r -n100 -P"$P" jq -c . >/dev/null

# transmettre plusieurs arguments par appel (-n2 ici)
paste -d' ' src.txt dest.txt \
  | xargs -r -n2 -P4 -I% sh -c 'set -Eeuo pipefail; cp -- %'

# contrôler les erreurs: arrêter le script si un job échoue (rc != 0)
set -o pipefail
git ls-files -z | xargs -0 -n1 -P"$P" -I{} bash -lc 'shellcheck -x "{}"' | cat
rc=${PIPESTATUS[1]}; [ "$rc" -eq 0 ] || exit "$rc"

# noms tordus (newlines, quotes): toujours -print0/-0 et guilleter "{}"
find . -type f -name "*.csv" -print0 \
  | xargs -0 -n1 -P4 -I{} sh -c 'set -Eeuo pipefail; wc -l -- "{}"'

notes

  • utilisez toujours -print0 côté find et -0 côté xargs pour des chemins sûrs (NUL-terminated).
  • -P fixe le nombre de jobs concurrents, -n le nombre d’arguments par invocation; ajustez selon la charge CPU/I/O.
  • -r évite de lancer la commande si l’entrée est vide (GNU xargs).
  • capturez le code de retour d’un xargs parallélisé via ${PIPESTATUS[...]} si vous êtes dans un pipeline; sinon, xargs renvoie 0 si tous les jobs réussissent, 123/124/125/126/127/255 selon les cas d’échec.
  • encapsulez dans sh -c 'set -Eeuo pipefail; ...' pour un fail-fast robuste par job et citez systématiquement "{}".