Lanci una copia o un'elaborazione lunga, e ti accorgi che servirà un'ora. Chiudi il terminale, o la connessione ssh cade, e il comando si ferma a metà. nohup serve a evitarlo: dice al programma di non arrendersi quando il terminale "riaggancia". È piccolo, vecchio e utilissimo, e si usa quando tmux (vedi l'articolo precedente) sarebbe eccessivo.
Cos'è
nohup (da no hang up, "non riagganciare") fa parte del pacchetto coreutils, quindi è presente su ogni distribuzione. Per capirne il senso serve un dettaglio: quando un terminale si chiude (perché lo chiudi tu, o perché la connessione cade) il sistema manda a tutti i programmi che stava gestendo un segnale chiamato SIGHUP (hangup, "riaggancio", come il telefono). Per impostazione predefinita, un programma che riceve SIGHUP si ferma. I segnali si vedono nell'articolo su ps e kill.
Cosa fa
nohup lancia un comando facendogli ignorare SIGHUP, e sistema l'ingresso e l'uscita perché non dipendano più dal terminale: l'ingresso viene scollegato, e l'uscita, se non l'hai già indirizzata altrove, viene scritta in un file chiamato nohup.out. Non fa altro: non manda il programma "in secondo piano" (per questo si aggiunge &) e non lo rende immune ad altri modi di essere fermato.
Come si usa
$ nohup comando [argomenti] &Gli esempi usano il server web01 degli articoli precedenti. Il comportamento di nohup, i messaggi e il fatto che il segnale ferma un programma normale e non uno protetto li ho provati in un sandbox; gli altri dettagli (copia, tempi) sono illustrativi.
Esempio 1: il problema
$ ./elabora-statistiche.sh
(la connessione ssh cade)
Connection to web01 closed by remote host.Il programma è stato lanciato dal terminale, e dipende da esso. Quando la sessione cade, il sistema invia SIGHUP al programma, che si ferma con il lavoro a metà: lo stesso accade se chiudi la finestra del terminale per sbaglio. Si può simulare: un sleep 120 lanciato in secondo piano muore se gli si manda kill -HUP, mentre lo stesso comando lanciato con nohup continua a vivere.
Esempio 2: il comando di base
$ nohup ./elabora-statistiche.sh &
[1] 4410
nohup: ignoring input and appending output to 'nohup.out'
$ tail -f nohup.out
$ exitCon nohup comando & il programma parte in secondo piano (&) e protetto. La shell stampa il numero del lavoro ([1]) e il numero del processo (4410, il PID); nohup avverte che sta ignorando l'ingresso e che scrive l'uscita in nohup.out, nella cartella in cui ti trovi (se non è scrivibile, nella tua cartella personale). Il file viene creato con permessi riservati al solo proprietario (600: vedi l'articolo su chmod e chown). Con tail -f (vedi l'articolo su head e tail) si segue l'uscita in tempo reale, e con exit ci si può anche scollegare: il programma continua.
Esempio 3: scegliere dove scrivere l'uscita
$ nohup ./elabora-statistiche.sh > statistiche.log 2>&1 &
$ nohup ./elabora-statistiche.sh > statistiche.log 2>&1 < /dev/null &
$ nohup ./elabora-statistiche.sh >> statistiche.log 2>&1 < /dev/null &
$ head -n 2 statistiche.log
nohup: ignoring input
Elaborazione iniziataAl posto del generico nohup.out è meglio indicare un file con un nome sensato: > file scrive l'uscita normale, 2>&1 manda nello stesso file anche gli errori (vedi l'articolo su echo e tee). Nella prima forma, la riga nohup: ignoring input finisce dentro il file. Per evitarla si scollega l'ingresso da soli con < /dev/null (seconda forma): l'avviso sparisce. Con >> (terza forma) il file non viene sovrascritto ma allungato, utile se lo stesso lavoro viene lanciato più volte. Le tre forme sono equivalenti per nohup: cambia solo come tieni in ordine i registri.
Esempio 4: controllare e fermare un lavoro
$ jobs
[1]+ In esecuzione nohup ./elabora-statistiche.sh > statistiche.log 2>&1 &
$ pgrep -af elabora-statistiche
4410 /bin/bash ./elabora-statistiche.sh
$ ps -o pid,etime,stat,cmd -p 4410
PID ELAPSED STAT CMD
4410 00:42 S /bin/bash ./elabora-statistiche.sh
$ kill 4410Finché sei nella stessa sessione, jobs elenca i lavori in secondo piano. Dopo esserti scollegato e ricollegato, non li vedi più, ma il programma c'è: lo trovi con pgrep -af (vedi l'articolo su ps) o con ps, che mostra anche da quanto tempo gira (ELAPSED). Per fermarlo, kill con il suo numero. Il programma non è più legato a un terminale: nella colonna TTY di ps compare un punto interrogativo (?).
Esempio 5: conservare il numero del processo
$ nohup ./elabora-statistiche.sh > statistiche.log 2>&1 < /dev/null &
$ echo $! > statistiche.pid
$ cat statistiche.pid
4410
$ kill "$(cat statistiche.pid)"La variabile $! contiene il numero dell'ultimo processo avviato in secondo piano. Salvarlo in un file permette di fermare il lavoro anche giorni dopo, senza doverlo cercare. È un'abitudine molto usata per i programmi lanciati a mano.
Esempio 6: più comandi insieme, con sh -c
$ nohup rsync -a /var/www/ /mnt/dati/www/ && echo finito &
$ nohup sh -c 'rsync -a /var/www/ /mnt/dati/www/ && echo finito' \
> copia.log 2>&1 < /dev/null &
$ cat copia.log
nohup: ignoring input
finitoAttenzione: nohup protegge un solo comando, quello che gli segue direttamente. Nel primo esempio protegge solo rsync (vedi l'articolo relativo), mentre echo finito viene dopo, fuori dalla protezione, e il tutto è lanciato nel modo sbagliato. Lo stesso vale per le pipe: in nohup cmd1 | cmd2 è protetto solo cmd1. Quando servono più comandi, pipe o &&, li si racchiude in sh -c '...' tra apici, così nohup protegge tutta la sequenza (secondo esempio, andato a capo con la barra rovesciata).
Esempio 7: partire da un lavoro già avviato, con disown
$ ./elabora-statistiche.sh
^Z
[1]+ Fermato ./elabora-statistiche.sh
$ bg %1
[1]+ ./elabora-statistiche.sh &
$ disown -h %1
$ exitSe ti accorgi dopo di aver lanciato un comando lungo senza nohup, non serve ricominciare. Ctrl+Z lo sospende, bg %1 lo fa proseguire in secondo piano, e disown -h %1 (un comando interno di bash) gli toglie il collegamento con la shell, così non riceve più SIGHUP alla chiusura. Un limite: l'uscita resta legata al terminale, per cui un programma che continua a scrivere sullo schermo può fermarsi o fallire quando il terminale sparisce. È un rimedio d'emergenza; meglio partire con nohup o con tmux.
Esempio 8: le alternative a nohup
$ setsid ./elabora-statistiche.sh > statistiche.log 2>&1 < /dev/null &
$ tmux new -d -s stat './elabora-statistiche.sh'
$ sudo systemd-run --unit=statistiche ./elabora-statistiche.sh
$ echo './elabora-statistiche.sh' | at 03:00Ci sono più modi di far vivere un programma oltre il terminale, e ognuno ha il suo uso: setsid avvia il comando in una sessione nuova, separata dal terminale (nel mio sandbox il processo risultava figlio del primo processo del sistema, non della shell); tmux permette anche di tornare a vedere e a usare il programma, cosa che con nohup non si può; systemd-run lo avvia come servizio temporaneo gestito da systemd (e poi si segue con systemctl e journalctl); at lo lancia una volta sola, a un'ora decisa. Per un lavoro che deve ripartire dopo un riavvio, la scelta giusta è un servizio vero o crontab con @reboot.
Esempio 9: uso negli script
#!/bin/bash
LOG="$HOME/copia.log"
PIDFILE="$HOME/copia.pid"
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then
echo "Già in esecuzione (PID $(cat "$PIDFILE"))" >&2
exit 1
fi
nohup rsync -a /var/www/ /mnt/dati/www/ >> "$LOG" 2>&1 < /dev/null &
echo $! > "$PIDFILE"
echo "Avviato, PID $!. Segui con: tail -f $LOG"Lo script avvia la copia in secondo piano e si ricorda del suo numero. Prima controlla se esiste già un processo con quel numero: kill -0 non manda nessun segnale, ma ha successo solo se il processo esiste, ed è il modo standard di chiederselo. Così non lancia due copie uguali per sbaglio. Alla fine dice come seguire l'avanzamento. Ho provato la logica con un comando finto (sleep): la seconda esecuzione si rifiuta di partire.
Opzioni utili
nohup comando &: la forma normale (in secondo piano, protetto da SIGHUP).> file 2>&1 < /dev/null: uscita e errori in un file scelto, ingresso scollegato (nessun avviso).$!: il numero dell'ultimo processo in secondo piano;jobs,pgrep -af,ps: per ritrovarlo.nohup sh -c '...': protegge una sequenza di comandi o una pipe.disown -h %1,bg, Ctrl+Z: salvare un lavoro già avviato senzanohup.setsid,tmux,screen,systemd-run,at,crontab: le alternative.kill -0 PID: verifica se un processo esiste;kill PID: lo ferma.
Attenzione a
- Protegge solo da SIGHUP: se qualcuno manda
kill(SIGTERM), se il sistema finisce la memoria (vedi l'articolo sufree) o se la macchina si riavvia, il programma si ferma comunque. Non sostituisce un servizio vero. - Serve il
&: senza, il comando resta in primo piano e il terminale è occupato finché non finisce; e se il terminale cade,nohuplo protegge, ma tu non hai più modo di vederlo. - Non può ricevere dati: l'ingresso viene scollegato, quindi un programma che fa domande (una conferma, una password) non può essere lanciato così: o si rende non interattivo con le opzioni apposite, o si usa
tmux. nohup.outcresce senza limiti: un programma molto loquace può riempire il disco (controlla condf -h, vedi l'articolo relativo). Indirizza l'uscita a un file con un nome chiaro e controlla ogni tanto la dimensione. Non dimenticare di cancellare i vecchinohup.outsparsi nelle cartelle.- Se l'uscita non è indirizzata, la connessione
sshpuò restare appesa: se un programma in secondo piano tiene aperto lo schermo, il comandoexitsembra non chiudere la sessione. Indirizza sempre l'uscita (> file 2>&1 < /dev/null) e il problema scompare. sudo nohuponohup sudo? Consudo nohup comando &il programma gira comeroot(enohup.outappartiene aroot); connohup sudo comando &sudopotrebbe chiedere la password, e non può farlo perché l'ingresso è scollegato. Fai prima l'autenticazione consudo -v, oppure usa la prima forma.- I servizi non si lanciano con
nohup: programmi comenginxo un database si avviano consystemctl, che si occupa di riavvii, registri e arresti ordinati. Alcuni programmi gestiscono SIGHUP a modo loro (rileggere la configurazione):nohupnon è la strada giusta. - A volte il sistema chiude comunque tutto all'uscita: alcune configurazioni di
systemd(KillUserProcesses=yes) terminano i processi di un utente quando l'ultima sessione si chiude, anche connohup. Se capita, usatmux,systemd-runo un servizio. - Un lavoro dimenticato resta attivo: ricordati di cercarlo con
pgrepepsse non finisce, e di chiudere ciò che non serve. Un programma in loop infinito consuma processore per giorni. - Gli errori di avvio compaiono subito: se il comando non esiste,
nohuprisponde connohup: failed to run command 'nome': No such file or directorye termina con codice 127 (126 se il file non è eseguibile). Per i comandi in secondo piano, verifica dopo qualche secondo conjobso con il registro.
Riepilogo
| Comando | Cosa fa |
|---|---|
nohup comando & | Lancia in secondo piano, protetto dalla chiusura del terminale |
nohup comando > log 2>&1 < /dev/null & | La forma più pulita: uscita nel file scelto, nessun avviso |
nohup sh -c 'a && b' & | Protegge una sequenza di comandi |
echo $! > lavoro.pid | Salva il numero dell'ultimo processo in secondo piano |
tail -f nohup.out | Segue l'uscita in tempo reale |
pgrep -af nome | Ritrova un lavoro dopo il ricollegamento |
kill $(cat lavoro.pid) | Ferma il lavoro salvato |
kill -0 PID | Verifica se il processo esiste |
Ctrl+Z, bg, disown -h %1 | Salva un lavoro già avviato senza nohup |
setsid comando | Avvia in una sessione separata dal terminale |
tmux new -d -s nome 'comando' | Come sopra, ma puoi tornare a guardarlo |
sudo systemd-run --unit=nome comando | Avvia come servizio temporaneo di systemd |