Linux / Commande nohup : poursuivre une tâche après déconnexion

nohup est un outil qui exécute une commande tout en ignorant le signal SIGHUP, qui peut être envoyé lorsque la connexion au terminal est perdue. Pour continuer une tâche après la déconnexion, il est courant d'utiliser nohup avec le symbole d'exécution en arrière-plan &, et il est également recommandé de spécifier explicitement les fichiers d'entrée et de sortie standards.

La forme suivante suppose que worker.sh existe et est exécutable. L’exemple reproductible ci-dessous fournit ce script, son lancement et la vérification du résultat.

nohup ./worker.sh > worker.log 2>&1 < /dev/null &

Cependant, nohup n'est pas un gestionnaire de services qui surveille ou redémarre les processus. Pour les services à long terme nécessitant un démarrage automatique après redémarrage, un redémarrage en cas de panne ou une rotation des journaux, des outils de gestion de services comme systemd sont plus appropriés.

Que fait la commande nohup ?

nohup exécute la commande spécifiée après l'avoir configurée pour ignorer le signal de hangup, SIGHUP. Lors de la fermeture d'un terminal ou de la fin d'une connexion SSH, SIGHUP peut être envoyé au shell et aux processus associés, et l'ignorer augmente la probabilité que la tâche continue de s'exécuter.

Comme on peut le voir dans l'explication officielle de GNU Coreutils sur nohup, nohup lui-même n'envoie pas une commande en arrière-plan. Pour continuer d'autres travaux dans le terminal actuel, vous devez ajouter séparément & à la fin de la commande.

Composants Ce qu'il fait Ce qu'il ne fait pas
nohup Fait que la commande à exécuter ignore SIGHUP. Ne s'exécute pas automatiquement en arrière-plan.
& Fait que le shell exécute la commande comme un travail en arrière-plan. Ne garantit pas l'ignorance de SIGHUP.
Redirection de sortie Envoie la sortie standard et les erreurs vers le fichier journal spécifié. N'impose pas de limites de taille de journal ni de suppression automatique.

Syntaxe de base et forme d'exécution recommandée

Syntaxe de base

Après nohup, écrivez la commande et les arguments à exécuter dans l'ordre.

nohup command [arg ...]

Par exemple, pour exécuter worker.sh dans le répertoire actuel, entrez comme suit.

nohup ./worker.sh

Cette forme ignore SIGHUP mais s'exécute au premier plan. Par conséquent, il est difficile de saisir la commande suivante dans le même shell jusqu'à ce que la commande se termine.

Exécution en arrière-plan et stockage des journaux

Dans le travail réel, il est plus facile à gérer si vous spécifiez l'exécution en arrière-plan et le chemin du journal ensemble.

nohup ./worker.sh > worker.log 2>&1 < /dev/null &
  • > worker.log écrit la sortie standard dans un nouveau fichier journal. Si le même fichier existe, il écrase le contenu existant.
  • 2>&1 envoie l'erreur standard au même endroit que la sortie standard actuelle.
  • < /dev/null détache l’entrée standard du terminal et renvoie immédiatement une fin de fichier à une tentative de lecture.
  • Le dernier & exécute la commande en arrière-plan.

Pour ajouter du nouveau contenu à un journal existant, utilisez >> au lieu de >.

nohup ./worker.sh >> worker.log 2>&1 < /dev/null &

Enregistrez le PID immédiatement après l'exécution

Juste après l'exécution d'une commande en arrière-plan, $! contient l'identifiant du processus (PID) du travail en arrière-plan le plus récemment démarré. Il est pratique d'enregistrer cette valeur dans un endroit sûr pour vérifier plus tard le statut ou terminer le processus.

nohup ./worker.sh > worker.log 2>&1 < /dev/null &
job_pid=$!
printf '%s\n' "$job_pid" > worker.pid

Placez le fichier PID dans un répertoire accessible en écriture uniquement à l’utilisateur qui gère la tâche. Un ancien PID peut avoir été réattribué à un autre processus : vérifiez la commande et l’heure de démarrage avant toute demande d’arrêt.

Exemple reproductible avec un petit script

Dans un répertoire où vous pouvez écrire, ce bloc crée un dossier temporaire et un script POSIX sh. Il nécessite Bash pour les commandes de lancement, ainsi que nohup, mktemp, chmod et sleep. Le script produit trois lignes puis se termine ; wait permet ici de récupérer son véritable code de sortie.

demo_dir=$(mktemp -d "$PWD/nohup-demo.XXXXXX") || exit 1
cd "$demo_dir" || exit 1
cat > worker.sh <<'EOF'
#!/bin/sh
step=1
limit=${1:-3}
while [ "$step" -le "$limit" ]; do
    printf 'étape %s\n' "$step"
    sleep 1
    step=$((step + 1))
done
EOF
chmod u+x worker.sh
nohup ./worker.sh > worker.log 2>&1 < /dev/null &
job_pid=$!
printf '%s\n' "$job_pid" > worker.pid
wait "$job_pid"
job_status=$?
cat worker.log
printf 'exit=%s\n' "$job_status"

Le résultat attendu est étape 1, étape 2, étape 3, puis exit=0. Pour observer une tâche plus longue, relancez nohup ./worker.sh 60 > worker.log 2>&1 < /dev/null & depuis le dossier d’essai et enregistrez à nouveau son PID. L’argument 60 change seulement le nombre d’itérations ; omettez-le pour revenir aux trois itérations par défaut. Si vous fermez le shell, notez le chemin absolu de ce dossier pour retrouver son journal et son fichier PID.

Comment les entrées et sorties standard sont-elles gérées ?

Entrée standard

Dans l’implémentation GNU, si l’entrée standard est connectée à un terminal, nohup la remplace par une source d’entrée illisible. Ce comportement réduit les situations où le programme s’arrête en attendant l’entrée après la disparition du terminal. Pour réduire les différences d’implémentation et clarifier l’intention, vous pouvez écrire explicitement < /dev/null dans la commande.

Sortie standard et nohup.out

Si la sortie standard est dirigée vers le terminal et qu’aucune redirection séparée n’est spécifiée, GNU nohup ajoute la sortie à nohup.out dans le répertoire courant. Si un fichier ne peut pas être créé dans le répertoire courant, il essaie nohup.out dans le répertoire personnel de l’utilisateur, et si les deux emplacements ne sont pas accessibles en écriture, la commande ne s’exécutera pas.

Si plusieurs tâches partagent le même nohup.out, il peut être difficile de distinguer à quelle tâche appartiennent les journaux, et le fichier peut continuer à croître. Il est préférable de spécifier un fichier journal avec un nom et une politique de rétention pour chaque tâche.

Erreur standard

Si l'erreur standard est dirigée vers le terminal, GNU nohup l'envoie généralement vers la même destination que la sortie standard. Cependant, selon le shell, le système d'exploitation ou les redirections précédemment appliquées, les lecteurs peuvent mal interpréter les résultats, il est donc plus sûr d'indiquer explicitement votre intention en utilisant 2>&1. L'ordre des connexions d'entrée/sortie dans le shell affecte le résultat, et les principes détaillés peuvent être consultés dans la manuel Bash sur les redirections.

Vérification du statut d'exécution et des journaux

Vérification des processus avec PID enregistré

Si vous avez le PID enregistré juste après l'exécution, vous pouvez le passer directement à ps pour vérifier son état.

job_pid=$(cat worker.pid)
ps -p "$job_pid" -o pid,ppid,stat,etime,cmd

PID est l'identifiant du processus, PPID est l'identifiant du processus parent, STAT est l'état, et ELAPSED est le temps d'exécution. Ne supposez pas qu'il s'agit de la tâche souhaitée simplement parce que le PID existe ; vérifiez également la colonne de commande à la fin.

Trouver un processus par nom

Si le PID n'a pas été enregistré, vous pouvez rechercher la ligne de commande avec pgrep.

pgrep -af 'worker.sh'

Puisque d'autres processus contenant la même chaîne peuvent également être trouvés, vous devriez vérifier toute la ligne de commande du résultat. Simplement rediriger la sortie de ps vers grep montre souvent la commande de recherche elle-même et n'est donc pas approprié pour l'automatisation.

Vérification des journaux en temps réel

L'utilisation de tail -f vous permet de voir en continu les ajouts à la fin d'un fichier.

tail -f worker.log

Pour arrêter la surveillance du journal, appuyez sur Ctrl+C. Cela ne termine que tail et ne stoppe pas la tâche originale écrivant le journal. Vous pouvez voir plus de détails sur la spécification du nombre de lignes et le suivi des fichiers dans comment utiliser la commande tail.

Terminer un processus démarré avec nohup

Tout d'abord, demandez une terminaison normale

Après avoir vérifié le PID cible et la commande, exécuter kill envoie essentiellement le signal SIGTERM. Si le programme contient du code de gestion de terminaison, il a la possibilité de fermer les fichiers et de nettoyer son état.

job_pid=$(cat worker.pid)
ps -p "$job_pid" -o pid,ppid,stat,etime,cmd
kill "$job_pid"

Vérifier s'il s'est terminé

Attendez un moment puis vérifiez à nouveau le même PID.

ps -p "$job_pid" -o pid,stat,etime,cmd

Si un script shell a créé plusieurs processus enfants, terminer seulement un parent peut laisser les enfants en cours d'exécution. Vérifiez d'abord l'arbre des processus et les groupes de processus, et si le programme que vous utilisez fournit une commande officielle de terminaison, donnez-lui la priorité.

Utilisez la terminaison forcée en dernier recours

Si vous forcez immédiatement la terminaison simplement parce qu'il ne répond pas à SIGTERM, les données peuvent ne pas être sauvegardées, ou les fichiers temporaires peuvent ne pas être nettoyés. Assurez-vous que la cause et la cible sont correctes, et utilisez SIGKILL seulement en dernier recours.

kill -KILL "$job_pid"

Exécution des pipelines et des commandes composées

Regroupement en une seule commande shell

Vous pouvez passer une chaîne de commande au shell pour exécuter un pipeline, plusieurs redirections, et des opérateurs conditionnels comme une seule tâche.

nohup sh -c 'printf "%s\n" alpha beta | tr "[:lower:]" "[:upper:]"' > pipeline.log 2>&1 < /dev/null &
pipeline_pid=$!
wait "$pipeline_pid"
cat pipeline.log

Après la fin du pipeline, pipeline.log contient les lignes ALPHA et BETA. Les commandes entre guillemets sont interprétées par le nouveau shell.

À ce moment, le pipeline à l'intérieur des guillemets est interprété par un nouveau shell. Si vous intégrez des entrées externes dans la chaîne de commande sans validation, il y a un risque d'injection de commande, donc ne pas insérer directement des valeurs non fiables. Vous pouvez également vous référer à Comment utiliser les opérateurs de connexion de commandes Bash (en anglais) pour la relation d'exécution des opérateurs shell.

Quand les journaux ne sont pas immédiatement visibles

Si le processus est en cours d'exécution mais que les journaux apparaissent tard, la cause pourrait être la mise en tampon de la sortie du programme plutôt que nohup. Tout d'abord, vérifiez les paramètres de vidage des journaux du programme lui-même. Dans les environnements basés sur GNU, si le programme et la méthode de sortie sont compatibles, vous pouvez demander un tampon de lignes avec stdbuf. Dans le modèle ci-dessous, remplacez ./worker par votre application : l’effet de stdbuf dépend de sa bibliothèque et de son mode d’écriture, pas seulement de la redirection du shell.

nohup stdbuf -oL -eL ./worker > worker.log 2>&1 < /dev/null &

stdbuf ne fonctionne pas pour tous les programmes. Si le programme réinitialise son propre tampon ou utilise une méthode d’entrée/sortie spécifique, il peut ne pas s’appliquer.

Limites de nohup et critères pour choisir d’autres outils

Fonctionnalités non fournies par nohup

nohup est un outil d’exécution simple qui modifie la gestion d’un signal. Les fonctionnalités suivantes ne sont pas fournies.

  • Démarrage automatique après le redémarrage du système
  • Redémarrage automatique lorsque le processus se termine anormalement
  • Gestion de l’ordre de démarrage des services dépendants
  • Limitation des ressources et surveillance de l’état tels que le CPU et la mémoire
  • Limitation de la taille des fichiers journaux, compression, conservation et suppression
  • Préservation des sessions de terminal interactives

Différences avec systemd, tmux, screen

Outils Situations appropriées Fonctionnalités
nohup Exécution temporaire de tâches batch non interactives Simple, mais n'a pas de fonctions de surveillance, de redémarrage ou de rotation des journaux.
systemd Services qui s'exécutent en continu sur le serveur Vous pouvez configurer l'intégration au démarrage, les politiques de redémarrage, les dépendances, et gérer les journaux et les ressources.
tmux (en anglais)/screen Tâches interactives qui doivent être reconnectées plus tard Vous pouvez détacher et reconnecter les sessions de terminal virtuel.
Planificateur de tâches Tâches à exécuter à un moment ou à un intervalle fixe Gérer le timing d'exécution et l'historique en utilisant cron ou une file de travaux dédiée.

Différence avec disown

disown est une fonctionnalité intégrée au shell dans certains shells comme Bash qui supprime un travail déjà démarré de la liste des travaux du shell ou modifie sa méthode de gestion du signal de fin. En revanche, nohup est un programme externe qui fait qu'une commande ignore SIGHUP dès son démarrage. Comme les options et le comportement de disown varient selon le shell, vous devez consulter la documentation du shell actuel pour des scripts portables.

Liste de contrôle pour une exécution sûre

  • Chemin : Spécifiez les scripts, les fichiers de configuration et les fichiers journaux par chemin absolu autant que possible. Cela garantit que les mêmes fichiers sont utilisés même si le lieu de connexion change.
  • Autorisations : Vérifiez si les mots de passe, les jetons ou les informations personnelles pourraient rester dans les journaux, et restreignez les autorisations des fichiers.
  • Taille : Configurez logrotate ou la politique de conservation des journaux du programme pour empêcher les journaux de croître continuellement.
  • Exécution en double : Vérifiez s'il est sûr que la même tâche s'exécute plusieurs fois simultanément. Si nécessaire, utilisez des fichiers de verrouillage ou la fonctionnalité d'exécution unique du programme.
  • Environnement : Lors d'une exécution non interactive, le répertoire de travail, PATH, la locale et les variables d'environnement peuvent différer de ceux du shell de connexion, donc spécifiez les valeurs nécessaires.
  • Arguments sensibles : Si vous passez des mots de passe ou des jetons en tant qu'arguments en ligne de commande, d'autres utilisateurs peuvent les voir dans la liste des processus. Utilisez des fichiers d'identification ou des méthodes de stockage sécurisé prises en charge par le programme.
  • Procédure de terminaison : Enregistrez le PID immédiatement après l'exécution et préparez à la fois la méthode de terminaison normale et les étapes pour vérifier les processus enfants restants.

Nettoyer le répertoire d’essai

Après avoir confirmé l’arrêt des tâches de démonstration, utilisez le chemin conservé dans demo_dir. Ces commandes suppriment seulement les fichiers créés dans cet exemple ; rmdir refuse de supprimer un dossier qui contient encore d’autres fichiers.

cd "$demo_dir/.." || exit 1
rm -- "$demo_dir/worker.sh" "$demo_dir/worker.log" "$demo_dir/worker.pid"
rm -f -- "$demo_dir/pipeline.log"
rmdir -- "$demo_dir"

Questions fréquemment posées

Un travail nohup persistera-t-il définitivement si je ferme la fenêtre SSH ?

Les commandes commençant par nohup ignorent SIGHUP, mais cela ne garantit pas absolument la persistance des tâches dans tous les environnements. Si un programme crée des processus enfants séparés tout en modifiant les paramètres de signal ou continue à utiliser le périphérique terminal, le résultat peut différer. Détachez à la fois l'entrée standard et la sortie standard du terminal, et pour les tâches importantes de longue durée, il est plus sûr d'utiliser un gestionnaire de services. Une politique de fermeture de session peut aussi arrêter les processus malgré nohup. Par exemple, KillUserProcesses=yes peut terminer le périmètre de session sous systemd-logind ; la documentation de logind.conf décrit cette condition.

Comment puis-je empêcher la création du fichier nohup.out ?

Vous pouvez spécifier directement la sortie standard et l'erreur standard. Seules les tâches qui ne nécessitent absolument aucun journal peuvent être envoyées à /dev/null, mais comme les informations de diagnostic seront également perdues, il est généralement recommandé d'utiliser des fichiers journaux séparés pour les tâches opérationnelles.

nohup ./worker > /dev/null 2>&1 < /dev/null &

Pourquoi les tâches n'apparaissent-elles pas dans jobs ?

jobs n'affiche que les tâches gérées par le shell actuel. Si vous vous déconnectez et vous connectez avec un nouveau shell, la liste des tâches du shell précédent ne se transfère pas, donc elle n'apparaîtra pas dans jobs. Utilisez le PID enregistré et ps, ou utilisez la fonction de vérification du statut des outils de gestion de services.

Que signifie le code de sortie de nohup ?

Avec GNU nohup, le code 125 signale un échec de nohup lui-même lorsque POSIXLY_CORRECT n’est pas défini, 126 indique une commande trouvée mais non exécutable et 127 une commande introuvable. Avec POSIXLY_CORRECT, les erreurs internes utilisent 127 au lieu de 125. Si le lancement réussit, le statut final est celui de la commande. Pour un travail en arrière-plan, utilisez wait dans le shell parent tant qu’il gère encore ce processus ; le $? d’un nouveau shell après reconnexion ne fournit pas le résultat de l’ancienne tâche.

Résumé

nohup exécute une commande de manière à ignorer SIGHUP, ce qui peut se produire lors de la déconnexion ou de la fermeture du terminal. Les tâches non interactives typiques utilisent nohup avec & et une redirection explicite de l'entrée/sortie standard, et l'enregistrement immédiat du PID après l'exécution facilite la gestion.

Si la tâche doit démarrer même après un redémarrage ou nécessite une récupération automatique en cas de panne, choisissez un gestionnaire de services comme systemd au lieu de nohup.