Nelle puntate precedenti abbiamo visto come scrivere dei semplici script in bash
o in awk
che risolvono un problema assai specifico, è vero, ma che possono anche costituire una buona base di partenza per affrontare argomenti più complessi (i link alle puntate precedenti si trovano alla fine dell’articolo).
In particolare, nella terza puntata abbiamo imparato come rendere i nostri script quasi indistinguibili dai normali comandi del Terminale: (1) si aggiunge in testa allo script lo shebang, cioè la sequenza di caratteri #!
, seguita dal percorso completo al programma da utilizzare per eseguire lo script,1 (2) si usa il comando chmod
per rendere lo script eseguibile (almeno) all’utente attuale del Mac,
$ chmod u+x nomescript
dove nomescript
è il nome generico dello script che vogliamo rendere eseguibile (ad esempio lo script convert_title.sh
della terza puntata).
Il “quasi” di prima dipende dal fatto che, anche con queste modifiche, ogni volta che eseguiamo lo script dobbiamo indicare esplicitamente la directory dove si trova. Ad esempio, se ci siamo spostati con cd
nella directory che contiene nomescript
, per eseguirlo script dobbiamo scrivere
$ ./nomescript
premettendo quindi al nome dello script il percorso (./
) alla directory corrente.
Invece, quando eseguiamo uno dei comandi del sistema operativo, ci basta scrivere il nome del comando nel Terminale e il sistema operativo (o più precisamente la shell che stiamo usando, che in genere è bash
)2 va a cercare il programma sul disco rigido e lo esegue senza troppi problemi.
Qual’è il meccanismo che permette alla shell di sapere in quale directory si trovano i comandi che eseguiamo nel Terminale? Esiste un modo per replicare questo comportamento anche per i nostri script, rendendoli praticamente indistinguibili dai comandi intrinseci del sistema operativo?
Variabili di ambiente
In realtà non c’è niente di sofisticato o di particolarmente intelligente in quello che fa la shell. La chiave di tutto sta nelle variabili di ambiente di bash
, delle variabili speciali usate fra l’altro per specificare le impostazioni di lavoro della shell. Se lanciamo il Terminale ed eseguiamo il comando
$ env
otterremo un lungo elenco con tutte le variabili di ambiente definite da bash
, che per convenzione vengono scritte in maiuscolo. Fra queste spiccano HOME
, che definisce il percorso alla cartella Home dell’utente che sta usando il Mac, USER
che riporta il nome account
(o nome breve
) dell’utente, e PWD
che contiene il percorso alla directory in cui ci troviamo (percorso che, come già visto, si può ricavare con il comando pwd
, ma guarda la combinazione).
Se siamo interessati solo ad una particolare variabile di ambiente, possiamo stamparla con echo
seguito dal nome della variabile di ambiente, che però deve essere preceduto dal simbolo $
. Ad esempio
$ echo $HOME
stampa nel Terminale il valore della variabile di ambiente HOME
.
Fra tutte le variabili di ambiente elencate da env
, quella che ci interessa di più in questo momento è PATH
, che contiene una serie di percorsi di directory separati dal simbolo :
. Nella configurazione di default il valore del PATH è
$ echo $PATH
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
Shell e PATH
È proprio il PATH che permette alla shell di trovare da sola i comandi di sistema. Ogni volta che eseguiamo un comando nel Terminale, la shell lo cerca nelle directory indicate nel PATH, esaminandole una dopo l’altra nell’ordine in cui sono scritte. Se trova il comando lo esegue immediatamente, senza preoccuparsi di esaminare le directory successive, altrimenti dà un errore di command not found
.
Di conseguenza, l’ordine in cui sono elencati i percorsi nel PATH è importante. Questa particolarità può essere sfruttata per sostituire un comando del sistema operativo con una versione più recente, senza però cancellare il comando originale: se il comando originale è situato in /usr/bin
o in /bin
, basta installare la versione aggiornata in una directory del PATH che precede queste due directory, come ad esempio /usr/local/bin
(che nei sistemi Unix serve proprio per installare il software “locale” non gestito direttamente dal sistema operativo).3
La seconda conseguenza è che, se modifichiamo il PATH aggiungendo altre directory in testa o in fondo all’elenco originale, possiamo obbligare la shell a cercare i comandi anche in queste nuove directory non presenti nella configurazione di default. Che è proprio quello che ci serve.
Prima di andare avanti una avvertenza FONDAMENTALE. Possiamo aggiungere senza troppi problemi delle nuove directory al PATH di default, ma non dobbiamo MAI MAI MAI rimuovere le directory presenti nella configurazione originale del PATH, in particolare le quattro directory /usr/bin:/bin:/usr/sbin:/sbin
(leggetele ad alta voce e notate l’assonanza). Cancellare per errore una di queste directory dal PATH significa non poter più usare il Mac, o perlomeno dover usare degli strumenti non alla portata di tutti per rimettere a posto le cose.
Siete avvertiti!!!
Modificare il PATH
Per aggiungere (o rimuovere) delle directory al PATH di default del nostro Mac dobbiamo modificare con un editor di testo (non con un wordprocessor!) il file .bashrc
situato nella nostra HOME
. Questo introduce una piccola complicazione, perché il file ~/.bashrc
è un file nascosto, non accessibile direttamente dal Finder.
Se usiamo emacs
o vi
possiamo aprire .bashrc
direttamente dal Terminale. Ci basta spostarci nella HOME
$ cd ~
ed eseguire
$ emacs .bashrc
oppure
$ vi .bashrc
Se questi editor ci sembrano troppo difficili da usare (e lo sono!), possiamo usare nano
che, come emacs
e vi
, si trova installato di default su ogni Mac
$ nano .bashrc
Anche se nano è un editor piuttosto limitato rispetto ai due mostri sacri, è molto comodo per modificare velocemente i file di sistema senza dover affrontare una lunga fase di apprendimento.
Se invece preferiamo usare un editor grafico come BBEdit, TextMate, Atom o simili, la procedura esatta per aprire un file nascosto dipende dall’editor scelto.
Alcuni editor permettono di installare un comando di Terminale con cui lanciare l’editor dalla linea di comando: mate
per TextMate, atom
e bbedit
per… lo sapete già. Anche con questi editor, quindi, possiamo aprire .bashrc
dal Terminale con uno dei comandi seguenti
$ mate .bashrc
$ atom .bashrc
$ bbedit .bashrc
Con BBEdit e TextMate, la finestra di apertura file dispone di un tasto Opzioni
con il quale si può decidere di mostrare (e quindi di aprire) anche i file nascosti
Ma il metodo più generale per modificare un file nascosto con un editor grafico è quello di usare il comando open
dal Terminale
$ cd ~
$ open -a "TextMate" .bashrc
dove l’opzione -a
permette di specificare l’applicazione con cui aprire il file indicato.
Qualunque sia il metodo utilizzato, alla fine ci ritroveremo con il file .bashrc
pronto per essere modificato. Tutto quello che dobbiamo fare è aggiungere questa linea alla fine di .bashrc
export PATH=$PATH:~/Development
che istruisce la shell di andare a cercare i comandi da eseguire nel Terminale anche nella directory ~/Development
, dove abbiamo deciso di salvare tutti gli script che sviluppiamo.
Una volta fatta questa semplice modifica, salviamo il file .bashrc
e usciamo dall’editor. Chiudiamo e riapriamo il Terminale per costringere la shell a rileggere il file .bashrc
(oppure eseguiamo il comando $ source ~/.bashrc
senza chiudere il Terminale) e proviamo di nuovo ad eseguire nomescript
, ma questa volta senza premettere il percorso alla directory dove si trova
$ nomescript
Questa volta lo script funzionerà senza bisogno di aiutini, proprio come se fosse un normale comando di sistema. Per verificare che tutto funzioni come descritto possiamo provare ad eseguire uno degli script della terza o della quinta puntata.
Ma non è ancora finita…
Bisogna ammetterlo, aggiungere al $PATH
una directory qualunque come ~/Development
può andar bene per fare delle prove veloci, ma non è per niente elegante. Sarebbe molto meglio adeguarsi alle convenzioni di Unix, secondo le quali i programmi eseguibili devono trovarsi in una directory denominata bin
.
Niente di più facile, possiamo creare facilmente una directory bin
nella nostra HOME
$ cd
$ mkdir bin
e decidere di tenere gli script in fase di sviluppo nella directory ~/Development
, per poi spostarli (o meglio copiarli) in ~/bin
quando sono pronti per essere usati. Naturalmente dovremo modificare di conseguenza il file .bashrc
, aggiungendo alla fine del file
export PATH=$PATH:~/bin
al posto di export PATH=$PATH:~/Development
visto sopra.
Non ci piace vedere con il Finder la directory bin
nella HOME
? Possiamo nasconderla con facilità, basta solo eseguire dal solito Terminale il comando
$ chflags hidden ~/bin
(il comando duale $ chflags nohidden ~/bin
rende di nuovo visibile la directory).
Conclusioni
Per i più pigri che non vogliono andare a rileggere le puntate precedenti per provare le novità descritte nell’articolo, ecco un semplice script in bash
che mostra la data e l’ora corrente nel formato standard Unix e in una forma molto più dettagliata (e per una volta anche nella nostra lingua)
#!/bin/bash
year=`date +%Y`
month=`date +%B`
day=`date +%d`
hour=`date +%H`
minute=`date +%M`
second=`date +%S`
dayofyear=`date +%j`
weekday=`date +%A`
echo `date`
echo -n "Oggi è $weekday $day $month $year, "
echo -n "il $dayofyear-esimo giorno dell'anno, "
echo "e sono le ore $hour:$minute:$second"
Copiamo lo script nell’editor preferito, salviamolo in ~/bin
(o in ~/Development
) con il nome data_estesa
, rendiamolo eseguibile con chmod
e proviamo che funzioni come previsto
$ data_estesa
Fri Apr 12 17:28:50 CEST 2019
Oggi è Venerdì 12 Aprile 2019, il 102-esimo giorno dell'anno, e sono le ore 17:28:50
E per oggi è davvero tutto.
Articoli precedenti
Per leggere gli articoli precedenti della serie basta cliccare sui link qui sotto.
- Prima puntata, Script per tutti i giorni: semplici modifiche alle stringhe di testo
- Seconda puntata, Script per tutti i giorni: dalla linea di comando al programma
- Terza puntata, Script per tutti i giorni: shell e parametri
- Quarta puntata, Script per tutti i giorni: ricapitoliamo
- Quinta puntata, Script per tutti i giorni: entra in scena awk
-
Sul Mac uno script in
bash
richiede il percorso/bin/bash
, uno inawk
puro/usr/bin/awk
, mentre uno nella ormai veneranda versione 2.7 dipython
installata di default in macOS richiede/usr/bin/python
(ma ci vuol poco ad installarepython3
con Homebrew o Conda). ↩ -
Ma ci sono anche
zsh
,ksh
,tcsh
,fish
, solo per menzionare le shell più note. ↩ -
È esattamente quello che fa Homebrew, uno strumento utilissimo (di cui ho scritto parecchio su questo blog) che utilizza
/usr/local/bin
per installare un gran numero di strumenti software provenienti dal mondo Linux e non disponibili su macOS, oppure per aggiornare a versioni più recenti quelli, normalmente piuttosto datati, forniti da Apple insieme al sistema operativo. ↩