Salta al contenuto principale

history: rivedere e riutilizzare i comandi già digitati

Inviato da tuxsa il

Hai scritto un comando lungo due giorni fa e non ricordi come? Hai digitato cat /etc/shadow e ti sei accorto di aver dimenticato sudo? Il terminale si ricorda tutto quello che scrivi, e history serve a ritrovarlo, a ripeterlo e a correggerlo senza riscriverlo.

Cos'è

history mostra la cronologia dei comandi digitati nella shell. In bash, la shell predefinita di quasi tutte le distribuzioni, la cronologia vive in due posti: in memoria, durante la sessione, e nel file ~/.bash_history, dove viene salvata quando la sessione termina e da cui viene riletta alla sessione successiva. Non è un programma a parte ma un comando interno della shell (come cd), e funziona insieme alle frecce, a Ctrl+R e a un gruppo di scorciatoie con il punto esclamativo, la cosiddetta history expansion.

Cosa fa

Elenca i comandi con un numero progressivo, permette di cercare tra quelli passati, di rieseguirne uno (o una parte) con poche battute e di cancellare le voci che non vuoi conservare. Il comportamento si regola con alcune variabili d'ambiente, di solito scritte in ~/.bashrc.

Come si usa

$ history [numero]
$ history [opzioni]

Gli esempi sono per bash; con zsh molte cose funzionano allo stesso modo, ma il file è ~/.zsh_history e le impostazioni sono diverse.

Esempio 1: vedere la cronologia

$ history | tail -n 5
  496  cd /var/www
  497  ls -l
  498  sudo nano /etc/nginx/nginx.conf
  499  sudo nginx -t
  500  history | tail -n 5
$ history 3
  499  sudo nginx -t
  500  history | tail -n 5
  501  history 3

Senza argomenti, history stampa tutta la cronologia, che può essere lunghissima: per questo si usa tail per vedere gli ultimi comandi o si passa a less. Con un numero (history 3) si vedono direttamente gli ultimi tre. Il numero a sinistra è l'identificativo del comando, che serve per rieseguirlo.

Esempio 2: cercare nella cronologia

$ history | grep nginx
  455  sudo apt install nginx
  498  sudo nano /etc/nginx/nginx.conf
  499  sudo nginx -t
  502  sudo systemctl reload nginx
$ history | grep -i "rsync" | tail -n 3
$ history | grep ssh | wc -l
14

Combinando history con grep si ritrova un comando di cui si ricorda una parola. Con -i la ricerca ignora maiuscole e minuscole, con tail si limita ai più recenti, con wc -l si contano.

Esempio 3: la ricerca interattiva con Ctrl+R

(premi Ctrl+R e scrivi qualche lettera)
(reverse-i-search)`rsy': rsync -avz progetto/ web:/home/enzo/progetto/

Ctrl+R è il modo più rapido: si premono i tasti e si comincia a scrivere una parte del comando, e la shell mostra l'ultimo comando che la contiene, aggiornandolo a ogni lettera. A quel punto:

  • Invio esegue il comando trovato;
  • freccia destra o Esc lo porta sulla riga per modificarlo senza eseguirlo;
  • Ctrl+R ancora una volta passa al risultato precedente (più vecchio);
  • Ctrl+G o Ctrl+C annulla la ricerca.

Esempio 4: muoversi con le frecce e ripetere l'ultimo comando

Freccia su / Ctrl+P    comando precedente
Freccia giù / Ctrl+N   comando successivo
$ !!                   riesegue l'ultimo comando
$ !-2                  riesegue il penultimo comando

Con le frecce si scorre la cronologia un comando alla volta e si può modificare prima di premere Invio. !! (due punti esclamativi) viene sostituito dall'ultimo comando, che è eseguito subito. !-2 è il penultimo, !-3 il terzultimo e così via.

Esempio 5: il classico "ho dimenticato sudo"

$ cat /etc/shadow
cat: /etc/shadow: Permesso negato
$ sudo !!
sudo cat /etc/shadow
root:$y$j9T$...:19650:0:99999:7:::
...

È l'uso più comune di !!: sudo !! riesegue il comando appena fallito con i privilegi di amministratore. La shell stampa il comando espanso prima di eseguirlo, così vedi cosa sta per succedere. Controlla sempre che sia davvero il comando che volevi.

Esempio 6: eseguire un comando per numero o per inizio

$ !499
sudo nginx -t
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ !sudo
sudo nginx -t
$ !?reload?
sudo systemctl reload nginx

!499 esegue il comando numero 499 dell'elenco. !testo esegue l'ultimo comando che comincia con quel testo (qui !sudo), e !?testo? l'ultimo che lo contiene in qualsiasi punto. È comodo ma può sorprendere: con !ssh parte l'ultima connessione, qualunque fosse. Per vedere cosa verrebbe eseguito, aggiungi :p alla fine (!sudo:p): il comando viene solo stampato e inserito in cronologia.

Esempio 7: riusare gli argomenti

$ mkdir -p progetti/2026/ottobre
$ cd !$
cd progetti/2026/ottobre
$ ls /var/log/nginx/access.log
$ nano !$
nano /var/log/nginx/access.log
$ cp backup.tar.gz /mnt/disco/
$ ls -l !:1
ls -l backup.tar.gz

!$ è l'ultimo argomento del comando precedente: perfetto per creare una cartella e poi entrarci. !:1 è il primo argomento e !* tutti. Esiste anche un modo con la tastiera: Alt+. (Alt e punto) inserisce l'ultimo argomento e, premuto più volte, risale ai precedenti. Rispetto a !$ ha il vantaggio che si vede il testo sulla riga prima di eseguirlo.

Esempio 8: correggere l'ultimo comando

$ sudo systemctl restart ngnix
Failed to restart ngnix.service: Unit ngnix.service not found.
$ ^ngnix^nginx
sudo systemctl restart nginx
$ !!:s/restart/status/
sudo systemctl status nginx

^vecchio^nuovo riesegue l'ultimo comando sostituendo la prima occorrenza di vecchio con nuovo: ideale per correggere un errore di battitura. !!:s/a/b/ fa lo stesso in forma estesa, e con :gs/a/b/ si sostituiscono tutte le occorrenze. Per correzioni più grosse si può digitare fc, che apre l'ultimo comando nell'editor predefinito (per esempio nano) e lo esegue al salvataggio.

Esempio 9: data e ora dei comandi

$ export HISTTIMEFORMAT="%F %T  "
$ history 3
  502  2026-10-25 07:41:12  sudo systemctl reload nginx
  503  2026-10-25 07:42:30  history 3
  504  2026-10-25 07:43:01  history 3

Per default la cronologia non registra l'orario. Impostando la variabile HISTTIMEFORMAT (con lo stesso formato di date) ogni comando mostra data e ora, utili per ricostruire "quando ho fatto quella modifica". Attenzione: i comandi eseguiti prima di aver impostato la variabile non hanno un orario e mostrano la data di avvio della sessione. Per renderla permanente va scritta in ~/.bashrc.

Esempio 10: cancellare una voce o tutta la cronologia

$ history -d 498
$ history -d 495-497
$ history -c
$ history -w
$ cat /dev/null > ~/.bash_history

history -d numero cancella una voce (e, nelle versioni recenti di bash, anche un intervallo come 495-497). history -c svuota la cronologia in memoria, ma il file ~/.bash_history resta com'era finché la sessione non termina o non si esegue history -w, che riscrive il file con il contenuto attuale. L'ultima riga svuota direttamente il file. Se hai scritto per sbaglio una password sulla riga di comando, il modo giusto è history -d sul numero giusto seguito da history -w: ma la password è già comparsa sullo schermo e può comparire altrove (vedi più avanti).

Esempio 11: impostare una cronologia più utile in ~/.bashrc

$ tail -n 7 ~/.bashrc
HISTSIZE=10000
HISTFILESIZE=20000
HISTCONTROL=ignoreboth:erasedups
HISTIGNORE="ls:cd:pwd:exit:clear"
HISTTIMEFORMAT="%F %T  "
shopt -s histappend
PROMPT_COMMAND="history -a; $PROMPT_COMMAND"
$ source ~/.bashrc

Cosa fa ciascuna riga:

  • HISTSIZE: quanti comandi tenere in memoria; HISTFILESIZE: quanti conservare nel file;
  • HISTCONTROL=ignoreboth:erasedups: non salva i comandi duplicati consecutivi né quelli che iniziano con uno spazio, ed elimina dalla cronologia le copie più vecchie dello stesso comando;
  • HISTIGNORE: elenco di comandi da non registrare;
  • HISTTIMEFORMAT: data e ora, come visto prima;
  • shopt -s histappend: alla chiusura la sessione aggiunge la sua cronologia al file, invece di sovrascriverlo;
  • PROMPT_COMMAND="history -a; ...": salva ogni comando nel file subito dopo l'esecuzione, e non solo alla chiusura.

Con source ~/.bashrc le modifiche valgono subito nella sessione corrente. Molte distribuzioni hanno già in ~/.bashrc alcune di queste righe, con valori più bassi (HISTSIZE=1000): controlla prima di aggiungere duplicati.

Esempio 12: più terminali aperti

$ history -a
$ history -n
$ history -r

Ogni terminale ha la propria cronologia in memoria, scritta nel file solo alla chiusura. Con più finestre aperte, i comandi di una non compaiono nelle altre. Con history -a si salva subito nel file la cronologia della sessione, con history -n si leggono nella sessione corrente i comandi aggiunti da altre finestre, e con history -r si rilegge tutto il file. La riga PROMPT_COMMAND dell'esempio precedente automatizza il salvataggio, non la lettura dalle altre finestre.

Esempio 13: statistiche e uso negli script

$ history | awk '{print $2}' | sort | uniq -c | sort -nr | head -n 5
    132 ls
     87 cd
     64 sudo
     41 nano
     29 systemctl
$ awk '{print $1}' ~/.bash_history | sort | uniq -c | sort -nr | head -n 3

Con le pipe si scopre quali sono i comandi che usi di più (vedi awk, sort e uniq). Il primo esempio funziona solo se la cronologia non ha l'orario: con HISTTIMEFORMAT impostato il comando si sposta in un'altra colonna. Il secondo legge direttamente il file e non ha questo problema, ma include solo i comandi già salvati.

Negli script la cronologia non è disponibile: history funziona solo nelle shell interattive, e un file di script ne ha una propria, vuota. Per analizzare la cronologia da uno script si legge il file:

#!/bin/bash
FILE="${1:-$HOME/.bash_history}"

echo "Comandi più usati in $FILE:"
awk '{print $1}' "$FILE" | sort | uniq -c | sort -nr | head -n 10

Se il file ha le date (con HISTTIMEFORMAT attivo, bash scrive righe come #1792886400 prima di ciascun comando), il conteggio le considererà come comandi. Per ignorarle si aggiunge un filtro: grep -v '^#' "$FILE" | awk ....

Opzioni utili

  • history N: ultimi N comandi.
  • -c: svuota la cronologia in memoria.
  • -d N: cancella la voce N (o un intervallo N-M).
  • -a: aggiunge al file i comandi di questa sessione; -w: riscrive il file con la cronologia in memoria.
  • -r: rilegge il file; -n: legge solo le righe nuove.
  • -p ... e :p: mostra senza eseguire (utile con la expansion).
  • Scorciatoie: !!, !N, !-N, !testo, !?testo?, !$, !:N, !*, ^a^b.
  • Tasti: Ctrl+R (cerca all'indietro), Ctrl+S (in avanti, se il terminale non la usa per sospendere l'output), Alt+. (ultimo argomento), Ctrl+P / Ctrl+N.
  • Variabili: HISTSIZE, HISTFILESIZE, HISTFILE, HISTCONTROL, HISTIGNORE, HISTTIMEFORMAT.
  • fc: apre un comando precedente nell'editor; fc -l elenca.

Attenzione a

  • La cronologia conserva tutto quello che scrivi, password e token compresi, se li hai digitati nella riga di comando (mysql -p'segreto', curl -u utente:password, export TOKEN=...). Il file ~/.bash_history è leggibile da chi accede al tuo account e dagli amministratori. Meglio far chiedere la password al programma, o leggerla da un file protetto; per un comando che contiene dati riservati, scrivi uno spazio all'inizio (funziona se HISTCONTROL contiene ignorespace o ignoreboth: controllalo con echo $HISTCONTROL).
  • La cronologia viene scritta alla chiusura della shell: se il terminale si blocca, il computer si spegne o chiudi con kill, i comandi della sessione possono andare persi. Con history -a o la riga PROMPT_COMMAND si salvano subito.
  • Senza histappend l'ultima sessione che si chiude vince: con più terminali aperti, ciascuno riscrive il file e cancella i comandi degli altri. Con shopt -s histappend i comandi si aggiungono.
  • Il punto esclamativo è speciale: dentro le virgolette doppie viene interpretato come history expansion. Una frase come echo "Ciao!" può dare l'errore event not found. Usa gli apici singoli (echo 'Ciao!') o disattiva la funzione con set +H. Negli script non è attiva.
  • sudo !! esegue davvero il comando di prima: è comodo, ma se l'ultimo comando era rm con un percorso sbagliato, lo ripeti con più privilegi. Leggi la riga che appare prima dell'esecuzione, o aggiungi :p per vederla soltanto.
  • !testo sceglie l'ultimo comando che comincia così, e può non essere quello che hai in mente. Per i comandi distruttivi (!rm, !dd) è una cattiva abitudine: usa Ctrl+R, che mostra il comando prima di eseguirlo.
  • history -c non basta a cancellare: svuota la memoria, ma il file resta. Serve anche history -w, oppure svuotare il file. E alla chiusura, se histappend è attivo, le righe già presenti nel file non vengono comunque toccate.
  • La cronologia non è un registro di sicurezza: ciascun utente ha la sua, la può modificare o cancellare, e non registra i comandi eseguiti dentro altri programmi (come un editor o una sessione mysql, che hanno una cronologia propria, per esempio ~/.mysql_history). Dopo sudo -i o su si lavora nella cronologia di root. Per un controllo vero su un server servono strumenti come auditd.
  • Il limite delle dimensioni: con HISTSIZE e HISTFILESIZE bassi (il valore tipico è 1000 o 2000) i comandi più vecchi spariscono. Se vuoi conservare tutto per anni, alza i valori o imposta un valore vuoto e archivia il file periodicamente.
  • I comandi con a capo: un comando su più righe (con la barra inversa o con un ciclo for) viene salvato come riga unica o spezzato, secondo l'impostazione cmdhist/lithist. Prima di rieseguirlo controlla che sia intero.
  • Altre shell, altre regole: zsh usa ~/.zsh_history e opzioni come setopt SHARE_HISTORY; fish ha un proprio sistema (history con altre opzioni). Le scorciatoie con ! funzionano in bash e zsh, non in fish.
  • Sul server vale quanto vale il tuo utente: ogni utente ha la sua cronologia. Per rivedere cosa ha fatto un collega con l'accesso condiviso non basta history: dopo un sudo il comando compare anche nel registro di sistema (/var/log/auth.log su Debian e Ubuntu).

Riepilogo

Comando / tastoCosa fa
historyMostra tutta la cronologia numerata
history 20Mostra gli ultimi 20 comandi
history | grep testoCerca un comando nella cronologia
Ctrl+RRicerca interattiva all'indietro
!!Riesegue l'ultimo comando
sudo !!Riesegue l'ultimo comando con sudo
!NRiesegue il comando numero N
!testoRiesegue l'ultimo comando che inizia così
!$ / Alt+.Ultimo argomento del comando precedente
^errato^correttoCorregge e riesegue l'ultimo comando
history -d NCancella una voce
history -cSvuota la cronologia in memoria
history -a / -wSalva nel file / riscrive il file
HISTTIMEFORMAT="%F %T "Mostra data e ora dei comandi
HISTCONTROL=ignorebothNon registra duplicati e comandi con lo spazio iniziale
fcModifica l'ultimo comando nell'editor