← retour aux snippets

timeout: limiter la durée d'une commande

Arrêter une commande après un délai, avec signal doux puis kill en dernier recours.


objectif

Empêcher une commande de bloquer indéfiniment en lui imposant un délai maximal, avec retour d’état explicite et escalade de signal.

code minimal

# stoppe la commande après 10 s (SIGTERM). exit 124 si délai dépassé.
timeout 10s commande_lente

utilisation

# délai 30 s, puis KILL au bout de 5 s supplémentaires si la commande n'a pas quitté
timeout -k 5s 30s ./job.sh

# envoyer un autre signal à l'expiration (ex: INT pour déclencher un handler)
timeout -s INT 15s make test

# protéger un pipeline entier
timeout 20s bash -lc 'producer | consumer'

# gérer les codes de sortie (124 = dépassé, 125 = erreur timeout, 126/127 = exec)
if timeout 5s curl -fsS https://data.pm/health >/dev/null; then
  echo "ok"
else
  rc=$?
  [ "$rc" -eq 124 ] && echo "time out" || echo "échec rc=$rc"
fi

# éviter les tampons bloquants sur stdout/stderr (GNU stdbuf)
stdbuf -oL -eL timeout 10s ./script_qui_bufferise

variante(s) utile(s)

# macOS/BSD: gtimeout via coreutils (brew install coreutils)
gtimeout 10s commande

# fallback portable sans (g)timeout (utilise perl, souvent présent)
with_timeout() {
  # usage: with_timeout 10 commande arg1 arg2...
  perl -e '
    $d=shift @ARGV; $SIG{ALRM}=sub{exit 124}; alarm $d; exec @ARGV or exit 127
  ' "$@"
}
with_timeout 10 sleep 20 || echo "rc=$?"

# escalade manuelle: INT puis KILL après 5 s
( sleep 10; pkill -INT -f 'serveur_local' ; sleep 5; pkill -KILL -f 'serveur_local' ) &
serveur_local
wait

# limiter un lot de tâches parallèles avec délai (xargs -P)
printf '%s\n' a b c | xargs -n1 -P4 -I{} timeout -k 2s 8s ./traite {}

notes

  • par défaut, timeout envoie SIGTERM à l’expiration; utilisez -k pour forcer un SIGKILL après un délai de grâce.
  • code de sortie: 124 si délai dépassé, 125 si timeout échoue, 126/127 pour erreurs d’exécution; sinon, c’est le code de la commande.
  • pour des commandes interactives, certains shells nécessitent bash -lc '...' afin de couvrir l’ensemble du pipeline.
  • sur macOS, utilisez gtimeout (coreutils) ou la fonction with_timeout ci-dessus en fallback.