– Foto: Trammell Hudson su Flickr.
Lo script di conversione del titolo di un post mostrato alla fine della puntata precedente è diventato ormai quasi “utilizzabile”. Mancano solo un paio di tocchi finali, che vedremo nel corso di questa terza parte.
Una casa per i programmi
Prima di proseguire è bene decidere una volta per tutte dove salvare gli script che stiamo sviluppando. Non so voi, ma io preferisco usare una cartella dedicata allo scopo invece di buttare tutto dove capita. In tutti gli articoli di questa serie gli script in fase di sviluppo saranno salvati nella cartella Development
, situata all’interno della cartella Home
(o Inizio
) dell’utente che ha effettuato il login (la cartella Home
è quella rappresentata dall’icona di una casetta). Ovviamente siete liberi di usare un altro nome e un’altra posizione sul disco rigido, ma dovrete ricordarvi di modificare di conseguenza i percorsi dei comandi.
Già che ci siamo, creiamo subito la cartella Development
. Se usate il Finder non ho bisogno di dirvi come si fa, se invece usate il Terminale basta eseguire i comandi
$ cd ~
$ mkdir Development
dove il primo comando, cd ~
(ma su macOS e varie versioni di Linux è sufficiente il semplice cd
) ci fa spostare nella nostra Home
, mentre il secondo comando crea la nuova directory Development
. Nelle shell Unix come bash
, il simbolo tilde ~
(che si scrive premendo ALT-5) indica la Home
dell’utente.
Volendo potremmo anche eseguire semplicemente
$ mkdir ~/Development
che crea la directory desiderata con un unico comando.
Qualche breve nota generale. In questo contesto i termini script o programma sono equivalenti e li userò indifferentemente. Lo stesso vale per directory e cartella. Io preferisco il primo, ma so benissimo che, da quando esistono le interfacce grafiche, il termine “cartella” è diventato di uso molto più comune. Nel seguito cercherò di usare “cartella” nel testo discorsivo degli articoli e “directory” quando si tratterà di riferirsi ai comandi di bash
e al Terminale. Infine, il simbolo $
prima di ogni comando è il cosiddetto prompt
e serve ad indicare che stiamo interagendo con il Terminale; non fa parte dei comandi e quindi non deve mai essere inserito quando scriviamo un comando nel Terminale.
Andare con le proprie gambe
Nella puntata precedente siamo arrivati ad usare uno dei tanti editor di testo disponibili su macOS per inserire queste righe di codice
string="La privacy al tempo dell'Internet of Things: gran finale"
fix_string=$(echo $string | tr "[:upper:]" "[:lower:]")
fix_string=$(echo $fix_string | sed "s/'/ /g")
fix_string=$(echo $fix_string | sed "s/[[:punct:]]//g")
fix_string=$(echo $fix_string | sed "s/ /-/g")
converted_string=$fix_string
echo $converted_string
Ora possiamo salvare lo script nella cartella ~/Development/
, dandogli un nome significativo che ci aiuti a ricordare anche in un secondo momento cosa fa il programma. Molto meglio quindi usare un nome come convert_title.sh
piuttosto che un criptico SCCUSIFJ.sh
, anche se profuma tanto di DOS e di anni ‘80.
Nei sistemi Unix come macOS l’estensione .sh
non è indispensabile ma ci aiuta a capire al volo che il file è uno script della shell (da cui .sh
). È possibile usare altre estensioni come .shell
, .script
, .cmd
o perfino .bat
, per il sistema operativo non cambia nulla.
A questo punto possiamo finalmente di eseguire lo script. Lanciamo il Terminale, spostiamoci nella cartella dove abbiamo salvato lo script
$ cd ~/Development
ed eseguiamo lo script con il comando
$ sh convert_title.sh
la-privacy-al-tempo-dell-internet-of-things-gran-finale
ottenendo, come previsto, la trasformazione della stringa definita nella string
nel formato adatto a Jekyll o a Wordpress.
Per eseguire lo script dobbiamo premettere al nome del file il comando sh
che indica al sistema operativo che il file in questione contiene una serie di comandi di shell, cioè di comandi che permettono di interagire con il sistema operativo stesso (attenzione, il comando sh
è una cosa ben diversa dall’estensione .sh
vista prima!). Con questa informazione, il sistema operativo trasferisce l’esecuzione dello script alla shell di default, che in macOS e nella maggior parte dei sistemi Linux attuali è bash
. Niente però impedisce di forzare l’utilizzo di bash
, scrivendo esplicitamente
$ bash convert_title.sh
al posto di sh convert_title.sh
, oppure di utilizzare altre shell eventualmente disponibili nel sistema, come tcsh
, ksh
, zsh
o fish
(argomento che non verrà trattato in questa serie).
Purtroppo affidarsi alla configurazione di default del sistema può creare problemi, ad esempio perché qualcuno ha cambiato la shell di default, magari solo per fare una prova, dimenticando di ripristinare la configurazione iniziale. Per fortuna in genere queste cose non succedono per cui possiamo stare relativamente tranquilli. Però il meccanismo appena descritto non è né semplice, né tanto meno a prova di bomba.
Proviamo a semplificarci la vita con due piccole modifiche. Prima di tutto utilizziamo il comando chmod
per rendere eseguibile lo script all’utente attuale del Mac
$ chmod u+x convert_title.sh
dove la u
indica l’utente che sta usando il Mac e il simbolo +x
significa che il file viene reso eseguibile (la x
sta per eXecute). Se volessimo rimuovere questa autorizzazione, non dovremmo far altro che eseguire il comando duale
$ chmod u-x convert_title.sh`
Se invece volessimo autorizzare tutti gli utenti del Mac ad eseguire questo script, dovremmo scrivere
$ chmod a+x convert_title.sh
mentre per autorizzare solo gli utenti che fanno parte del nostro stesso gruppo di lavoro oppure tutti gli altri utenti fuori dal nostro gruppo di lavoro, dobbiamo usare rispettivamente chmod g+x convert_title.sh
oppure chmod o+x convert_title.sh
.
Ma queste sono finezze utili per i server a cui accedono decine e decine di utenti contemporaneamente, i Mac sono sostanzialmente macchine monoutente (o che gestiscono un numero molto ridotto di utenti), per cui chmod u+x
(o al massimo chmod a+x
) a noi basta e avanza.
L’altra modifica utile è quella di indicare esplicitamente al sistema operativo quale shell vogliamo utilizzare per interpretare i comandi contenuti nel nostro script. Per farlo è sufficiente aggiungere all’inizio dello dello script la sequenza di caratteri #!
, il cosiddetto shebang,1 seguita dal percorso completo al programma di shell che vogliamo usare.
Dato che qui usiamo bash
, il nostro script diventa
#!/bin/bash
string="La privacy al tempo dell'Internet of Things: gran finale"
fix_string=$(echo $string | tr "[:upper:]" "[:lower:]")
fix_string=$(echo $fix_string | sed "s/'/ /g")
fix_string=$(echo $fix_string | sed "s/[[:punct:]]//g")
fix_string=$(echo $fix_string | sed "s/ /-/g")
converted_string=$fix_string
echo $converted_string
Ma come lo troviamo il percorso di bash
? C’è un comando anche per questo: which
seguito dal nome di un qualunque comando Unix restituisce proprio il percorso completo del comando all’interno del filesystem. Di conseguenza which bash
restituisce
$ which bash
/bin/bash
che è proprio ciò che dobbiamo inserire in testa allo script subito dopo lo shebang.
Per verificarlo, basta provare a salvare lo script modificato con un nome diverso, diciamo convert_title_v2.sh
e rendiamolo esegubile con il comando chmod a+x convert_title_v2.sh
descritto prima.2 A differenza di convert_title.sh
, questo secondo script potrà essere eseguito senza dover premettere sh
$ ./convert_title_v2.sh
la-privacy-al-tempo-dell-internet-of-things-gran-finale
Per una serie di ragioni che vedremo in una prossima puntata, dobbiamo però aiutare il sistema operativo a trovare lo script aggiungendo esplicitamente il percorso allo script da eseguire. Poiché nei sistemi Unix (e non solo), il punto .
indica la cartella corrente (mentre il doppio punto ..
si riferisce alla cartella che contiene la cartella corrente), scrivere ./
immediatamente prima del nome dello script equivale a dire al sistema operativo che lo script si trova nella cartella corrente, che in questo caso è la cartella dove siamo entrati all’inizio di questa puntata con il comando cd ~/Development
.
Parametri sulla linea di comando
C’è ancora una cosa che non va bene. Ogni volta che vogliamo cambiare la stringa da convertire dobbiamo intervenire direttamente sullo script, modificando la variabile string
. Facendo così è facile sbagliare, e in ogni caso sarebbe molto più comodo rendere lo script indipendente dai dati sui quali deve operare (in questo caso la stringa da convertire).
Non voglio entrare nei dettagli (ma ci sarà tempo per approfondire la questione), per ora basterà dire che per rendere lo script indipendente dalla stringa da trasformare è sufficiente assegnare a string
il valore della variabile convenzionale $1
#!/bin/bash
string=$1
fix_string=$(echo $string | tr "[:upper:]" "[:lower:]")
fix_string=$(echo $fix_string | sed "s/'/ /g")
fix_string=$(echo $fix_string | sed "s/[[:punct:]]//g")
fix_string=$(echo $fix_string | sed "s/ /-/g")
converted_string=$fix_string
echo $converted_string
L’interprete bash
assegna automaticamente alle variabili $1
, $2
, … $9
, dette parametri posizionali, i valori degli argomenti dello script, cioè i valori dei parametri presenti sulla linea di comando subito dopo il nome dello script da eseguire.3 I diversi parametri sono separati da uno o più spazi, e se un parametro contiene degli spazi bisogna racchiuderlo fra virgolette, non importa se semplici '
o doppie "
(però è preferibile essere consistenti ed evitare di mescolare i due simboli).
Salviamo lo script così modificato con il solito nome convert_title.sh
e torniamo al Terminale. Ora per usare lo script dobbiamo scrivere la stringa da convertire direttamente sulla linea di comando, subito dopo il nome del file, racchiudendola fra virgolette dato che la stringa contiene degli spazi (io preferisco usare le virgolette doppie)
$ ./convert_title.sh "La privacy al tempo dell'Internet of Things: gran finale"
la-privacy-al-tempo-dell-internet-of-things-gran-finale
Naturalmente niente impedisce di trasformare stringhe molto più lunghe, come ad esempio un paragrafo di questo stesso articolo, l’unica limitazione è che la stringa non deve contenere dei caratteri di ritorno a capo
$ convert_title.sh "Purtroppo affidarsi alla configurazione di default del sistema può creare problemi, ad esempio perché qualcuno ha cambiato la shell di default, magari solo per fare una prova, dimenticando di ripristinare la configurazione iniziale. Per fortuna in genere queste cose non succedono per cui possiamo stare relativamente tranquilli. Però il meccanismo appena descritto non è né semplice, né tanto meno a prova di bomba."
purtroppo-affidarsi-alla-configurazione-di-default-del-sistema-può-creare-problemi-ad-esempio-perché-qualcuno-ha-cambiato-la-shell-di-default-magari-solo-per-fare-una-prova-dimenticando-di-ripristinare-la-configurazione-iniziale-per-fortuna-in-genere-queste-cose-non-succedono-per-cui-possiamo-stare-relativamente-tranquilli-però-il-meccanismo-appena-descritto-non-è-né-semplice-né-tanto-meno-a-prova-di-bomba
Se guardiamo attentamente il risultato della conversione notiamo come lo script non riesce a trattare correttamente i caratteri accentati, come era stato già notato esplicitamente nella prima puntata di questa serie di articoli. Per ora sarà sufficiente evitare di usare i caratteri accentati o almeno scriverli con una vocale seguita da un apostrofo, come se stessimo usando una tastiera americana.
Conclusioni (per ora)
Se togliessimo l’estensione al nome del file, lo script convert_title
diventerebbe quasi indistinguibile dai comandi veri e propri del sistema operativo. Dico “quasi” perché per poterlo eseguire dobbiamo ancora premettere il percorso alla cartella corrente al nome del file. Ci sarà modo per risolvere questo (piccolo) problema, i più impazienti possono leggere qui per capire in anteprima come si fa.
La prossima volta faremo un veloce ripasso dei comandi presentati in queste prime tre puntate, utile per consolidare quanto visto finora prima di passare ad usare uno degli strumenti più potenti ma anche meno conosciuti dell’arsenale Unix.
Revisioni
30-12-2018: Corretta una imprecisione relativa al cambiamento dei permessi dei file quando la prima riga di uno script contiene lo shebang e il percorso all’interprete bash
(si veda la nota 2).
-
Detto anche sha-bang o hashbang. ↩
-
Se la stringa
#!/bin/bash
è esattamente la prima riga dello script, quando salviamo lo script l’editor TextMate lo rende eseguibile da tutti gli utenti del sistema, proprio come se avessimo usato esplicitamente il comandochmod a+x ...
. È una particolarità del solo TextMate, tutti gli altri editor che ho provato (Atom, BBEdit, Visual Studio Code e CotEditor fra quelli grafici, nano, emacs e vim per quanto riguarda gli editor testuali) non modificano mai i permessi del file. ↩ -
Se abbiamo bisogno di più di nove parametri, dobbiamo racchiudere quelli dal decimo in poi fra parentesi graffe, scrivendo
${10}
,${11}
e così via. ↩