Un comando lanciato dalla shell tiene occupato il terminale finché non finisce. Il job control serve a decidere chi sta davanti, chi lavora dietro e chi sopravvive alla chiusura della sessione. Attenzione al vocabolario: un job è un processo avviato in modo interattivo da quel terminale e non ancora concluso, e vive solo dentro quella shell.

Mandare in background: & e jobs

Una e commerciale & in fondo alla riga avvia il comando in background e ti restituisce subito il prompt.

$ sleep 300 &
[1] 970
$ jobs
[1]+  Running                 sleep 300 &
$ jobs -l
[1]+   970 Running                 sleep 300 &

Compaiono due numeri diversi: [1] è il job ID, che appartiene alla shell, e 970 è il PID, che appartiene al sistema. Il job ID si usa preceduto dal segno di percentuale. Le opzioni di jobs: -l aggiunge il PID, -p mostra solo i PID, -r solo i job in esecuzione, -s solo quelli sospesi, -n solo quelli che hanno cambiato stato.

Le specifiche di job sono materia da esame: %1 per numero, %+ (oppure %%) per il job corrente, %- per il precedente, %sl per un comando che inizia per «sl», %?le per uno che contiene «le». Il + e il - che vedi nell’elenco indicano proprio quei due.

Ctrl+Z, fg e bg

Ctrl+Z sospende il processo in primo piano inviandogli SIGTSTP: lo stato diventa Stopped, il programma esiste ancora ma non gira.

$ sleep 300
^Z
[1]+  Stopped                 sleep 300
$ bg %1
[1]+ sleep 300 &
$ fg %1
sleep 300

bg fa ripartire il job in background, fg lo riporta in primo piano. Senza argomenti agiscono sul job corrente, quello marcato con +; kill invece una specifica la vuole sempre. Due dettagli pratici: un job in background che prova a leggere dalla tastiera si ferma da solo, e il suo output continua a comparire sopra il tuo prompt se non lo reindirizzi in un file.

Sopravvivere al logout: nohup, screen e tmux

Alla chiusura del terminale la shell manda SIGHUP ai suoi job. nohup rende il comando immune a quel segnale:

$ nohup ./importa.sh &
[1] 1251
nohup: ignoring input and appending output to 'nohup.out'
$ nohup ./importa.sh > /var/log/importa.log 2>&1 &

Se lo standard output è un terminale, nohup dirotta output ed errori nel file nohup.out della directory corrente (nella home se questa non è scrivibile). Dopo il logout il processo non è più un job: jobs non lo elenca e per fermarlo serve il PID. Bash offre anche disown -h, che protegge un job già avviato.

Quando il lavoro è interattivo (un aggiornamento lanciato via SSH) conviene un multiplexer di terminale: screen o tmux. Stacchi la sessione con Ctrl+a d oppure Ctrl+b d, i programmi continuano a girare, e la riprendi con screen -r o tmux attach -t nome; l’elenco si ottiene con screen -ls e tmux ls. Nota per i sistemi con systemd: se logind è configurato con KillUserProcesses attivo, i processi residui vengono chiusi al logout e nemmeno nohup basta.

kill e i segnali

kill non «uccide»: invia un segnale, per default SIGTERM (15), che chiede al processo di chiudersi in modo ordinato. Il segnale si indica per nome, per numero o con -s, e il bersaglio può essere un PID o una specifica di job.

$ kill 1251
$ kill -HUP 1251
$ kill -1 1251
$ kill -s SIGKILL 1251
$ kill %1
$ kill -l

I segnali da ricordare: SIGHUP (1), chiusura del terminale e, per molti demoni, «rileggi la configurazione»; SIGINT (2), il Ctrl+C; SIGTERM (15), l’uscita gentile; SIGKILL (9) e SIGSTOP, che non possono essere intercettati né ignorati e quindi funzionano sempre, ma senza dare al processo il tempo di salvare e di chiudere i suoi file. La regola operativa è: prima TERM, e solo se non risponde KILL. Nome e numero dei tre segnali principali sono da sapere a memoria.

Uccidere per nome: killall e pkill

$ killall sleep
$ pkill -HUP nginx
$ pkill -f 'python3 backup.py'
$ pkill -u carol firefox

killall vuole il nome esatto dell’eseguibile e colpisce tutte le istanze. pkill lavora per corrispondenza parziale sul nome, con -f cerca dentro l’intera riga di comando e con -u si limita a un utente. Proprio perché la corrispondenza è parziale è facile centrare più processi del previsto: la prova a vuoto si fa con pgrep e le stesse opzioni, che si limita a stampare. Ricorda infine che killall e pkill non accettano le specifiche di job: quelle le capisce solo kill.