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
-print0côtéfindet-0côtéxargspour des chemins sûrs (NUL-terminated). -Pfixe le nombre de jobs concurrents,-nle 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
xargsparallélisé via${PIPESTATUS[...]}si vous êtes dans un pipeline; sinon,xargsrenvoie 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"{}".