giovedì 21 novembre 2013

Per conoscere la Storia bisogna prima imparare a leggere ... una linea alla volta

Per chi come me è una vecchia ciabatta nanica, burbera e nostalgica, la Storia ha un valore inestimabile. Come sapevano bene già gli antichi Romani, la Storia è magistra vitae e saper fare riferimento ad essa e al patrimonio di conoscenza che contiene può essere una risorsa davvero importante, anche per chi non è un consumato stregone da tastiera ... Come dite? Non esistono nani stregoni? Aspettate e vedrete.

Una delle funzionalità più misconosciute della shell è senza dubbio la command line history (history).
Ogni qual volta digitiamo un comando in una shell questo viene automaticamente registrato in una lista detta appunto history. Quando terminiamo la sessione di lavoro con la shell, questa lista viene scritta nel file designato dalla variabile d'ambiente HISTFILE. Se questa variabile è vuota o non è impostata, di default viene usato un file nella home utente (generalmente .history, .bash_hisotry, .zsh_history o qualcosa di simile, a seconda della shell che utilizzate).
La funzione principale della history non è comunque quella di lasciare delle tracce indelebili delle vostre malefatte acciocché qualcuno possa farsi gli affaracci vostri. Lo scopo è quello di fornire all'utente un modo conveniente e comodo per evitare di scrivere e riscrivere le stesse linee di comando, in special modo quando si sta facendo un lavoro ripetitivo nella shell.

Per visualizzare il contenuto della history è sufficiente digitare history o history N per visualizzare soltanto le ultime N righe della lista.

utente@localhost:~$ history
1  cp testfile.txt testfile2.txt
2  mv testfile1.txt testfile.ori
3  ls -la /home/utente
4  scp testfile.txt utente@nuovohost:/home/utente
5  su -
6  crontab -l
7  exit
8  history

Come potete vedere a fianco di ogni linea registrata è indicato un numero di riferimento. Se volete avere traccia anche della data e dell'ora di sistema associate ad una certa linea della lista, occorre impostare una variabile d'ambiente specifica con il formato strftime. A tal scopo potete aggiungerla al vostro .profile (o .bash_profile, .zsh_profile o quello che usa la vostra shell preferita)

export HISTTIMEFORMAT="%d/%m/%Y %H:%M -> "

E a questo punto la lista dovrebbe avere un aspetto simile al seguente:

utente@localhost:~$ history
1  18/11/2013 10:30 ->  cp testfile.txt testfile2.txt
2  18/11/2013 10:36 ->  mv testfile1.txt testfile.ori
3  18/11/2013 10:50 ->  ls -la /home/utente
4  19/11/2013 07:58 ->  scp testfile.txt utente@nuovohost:/home/utente
5  19/11/2013 09:03 ->  su -
6  20/11/2013 12:10 ->  crontab -l
7  20/11/2013 16:40 ->  exit
8  20/11/2013 16:43 ->  history

Se invece siete davvero preoccupati che quel cattivone di root possa sbirciare i comandi che usate nella shell, potete ripulirla con il comando history -c. Attenzione però! Tale modifica verrà finalizzata e archiviata nel vostro file di log della history soltanto quando avrete terminato la sessione effettuando il logout. Fino a quel momento, all'interno del file di log, sarà ancora presente l'history aggiornata al vostro ultimo logout.

Come dicevo, la history è utile per evitare di riscrivere intere linee di comando. Quando vogliamo rilanciare una linea data in precedenza possiamo farlo semplicemente usando il punto esclamativo seguito dal numero di riferimento.

utente@localhost:~$ !3
ls -la /home/utente

Se vogliamo velocemente ripetere l'ultimo comando dato senza dover ricordare o sbirciare il numero di riferimento, è disponibile un comodo shortcut, il doppio punto esclamativo.

utente@localhost:~$ !!
ls -la /home/utente

Se come numero di riferimento usiamo un valore negativo, verrà interpretato come un offset rispetto alla coda della lista, contando a ritroso. Così, se vogliamo rilanciare il comando dato tre linee indietro (il terzultimo in ordine temporale), possiamo farlo usando -3 come riferimento.

utente@localhost:~$ !-3
crontab -l

A volte scorrere tutta la lista per trovare un comando dato in precedenza può essere tedioso. Usando sempre il punto esclamativo ma questa volta seguito dall'iniziale del comando che vogliamo rilanciare, verrà eseguita la linea che contiene l'occorrenza più recente.

utente@localhost:~$ !s
su -

Se vogliamo sapere in anticipo quale delle corrispondenze possibili verrà eseguita, possiamo utilizzare lo switch -p, che visualizzerà la linea senza eseguirla.

utente@localhost:~$ history -p !s
history -p su -
su
-

Fin qui, si potrà obiettare, nulla di magico. La maggior parte di noi, anche senza esserne forse consapevole, usa già la history attraverso la navigazione con i tasti direzionali. Questa funzionalità infatti è garantita dall'uso della libreria readline che ormai è abbastanza standard un po' in tutte le shell. Oltre a navigare nella history con i tasti direzionali, readline ci consente anche di editare e modificare poi le linee di comando così recuperate, e di fare anche altre ... magie.

Per eseguire una ricerca nella history possiamo utilizzare delle combinazioni di tasti predefinite. Eccone alcune:

ALT + p (ricerca all'indietro)
ALT + n (ricerca in avanti)
CTRL + r (ricerca incrementale all'indietro)
CTRL + s (ricerca incrementale in avanti)

Nel caso della ricerca incrementale il comando recuperato dalla lista può essere inserito premendo CTRL + j in modo da poterlo eventualmente editare. Premendo CTRL + o viene invece eseguito il match correntemente visualizzato e si avanza al comando successivo nella lista (utile per rieseguire una sequenza di comandi). Per annullare la ricerca è infine sufficiente CTRL + c.

Infine due trucchetti per trarre il massimo dalla Storia del vostro lavoro nella shell. La libreria readline può leggere un file di configurazione personalizzato per ogni utente. Tale file deve chiamarsi ~/.inputrc e può contenere le vostre personalizzazioni delle combinazioni di tasti per la history (e per ogni altra funzione legata a readline, date un occhio alla pagina di manuale).
Create quindi il vostro file di configurazione e inserite le seguenti linee al suo interno:

"\e[A": history-search-backward
"\e[B": history-search-forward

Riavviate la sessione della shell o provate a farle rileggere ~/.inputrc con la combinazione CTRL + x. La configurazione appena fatta vi consentirà di utilizzare i tasti direzionali per eseguire una ricerca incrementale nella history. I risultati verranno proposti quindi sulla riga di comando a partire dalla posizione attuale del cursore e verrà utilizzato quanto avete già digitato come stringa da ricercare.

E per finire script. Questo semplice comando vi permetterà di registrare in un file un'intera sessione di lavoro nella shell, per farne un vero e proprio script all'occorrenza o per documentare il vostro lavoro. Lanciate quindi script filedilog prima di iniziare e CTRL + d per terminare. Troverete all'interno di filedilog l'intera cronologia della sessione di lavoro.