3.3 Lezione 1
Certificazione: |
Linux Essentials |
---|---|
Versione: |
1.6 |
Argomento: |
3 Il Potere della Command Line |
Obiettivo: |
3.3 Trasformare i Comandi in uno Script |
Lezione: |
1 di 2 |
Introduzione
Finora abbiamo imparato a eseguire i comandi dalla shell, ma possiamo anche inserirli in un file e poi renderlo eseguibile. Quando viene lanciato il file, questi comandi vengono eseguiti in sequenza uno dopo l’altro. Questi file eseguibili sono chiamati script e sono uno strumento assolutamente indispensabile per qualsiasi amministratore di sistema Linux. In sostanza, possiamo considerare Bash un linguaggio di programmazione oltre che una shell.
Stampare l’output
Iniziamo mostrando un comando che potresti aver visto nelle lezioni precedenti: echo
stampa un argomento sullo standard output.
$ echo "Hello World!" Hello World!
Useremo ora la redirezione dei file per inviare questo comando a un nuovo file chiamato new_script
.
$ echo 'echo "Hello World!"' > new_script $ cat new_script echo "Hello World!"
Il file new_script
ora contiene lo stesso comando di prima.
Rendere uno Script Eseguibile
Mostriamo alcuni dei passaggi necessari per far sì che questo file funzioni nel modo previsto. La prima idea di un utente potrebbe essere quella di digitare semplicemente il nome dello script, proprio come digiterebbe il nome di un qualsiasi altro comando:
$ new_script /bin/bash: new_script: command not found
Possiamo tranquillamente supporre che new_script
esista nella nostra posizione corrente; nota però che il messaggio di errore non ci dice che il file non esiste, ma che il comando non esiste. Potrebbe essere utile discutere di come Linux gestisce i comandi e gli eseguibili.
Comandi e PATH
Quando per esempio digitiamo il comando ls
nella shell, eseguiamo un file chiamato ls
che esiste nel nostro filesystem. Per dimostrarlo, si può usare which
:
$ which ls /bin/ls
Diventerebbe subito noioso digitare il percorso assoluto di ls
ogni volta che si desidera esaminare il contenuto di una directory: per questo motivo Bash ha una variabile d’ambiente che contiene tutte le directory in cui potremmo trovare i comandi che desideriamo eseguire. Puoi visualizzare il contenuto di questa variabile usando echo
.
$ echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
In ciascuna di queste posizioni, separate tra loro dal carattere due punti (:
), la shell si aspetta di trovare i comandi. Avrai notato che /bin
è presente, ma è lecito ritenere che la nostra posizione corrente non lo sia. La shell cercherà new_script
in ciascuna di queste directory, ma non lo troverà e quindi mostrerà il messaggio di errore visto in precedenza.
Ci sono tre soluzioni a questo problema: possiamo spostare new_script
in una delle directory presenti nella variabile PATH
; possiamo aggiungere la nostra directory corrente a quelle presenti nella variabile PATH
; oppure possiamo cambiare il modo in cui chiamiamo lo script. L’ultima soluzione è la più semplice: richiede semplicemente di specificare la posizione corrente quando si chiama lo script, utilizzando una barra obliqua dopo un punto (./
).
$ ./new_script /bin/bash: ./new_script: Permission denied
Il messaggio di errore è cambiato, il che indica che abbiamo fatto dei progressi.
Permessi di Esecuzione
La prima indagine che un utente dovrebbe fare in questo caso consiste nel dare un’occhiata al file tramite ls -l
:
$ ls -l new_script -rw-rw-r-- 1 user user 20 Apr 30 12:12 new_script
Possiamo vedere che i permessi di questo file sono impostati di default su 664
. Non abbiamo infatti ancora impostato i permessi di esecuzione.
$ chmod +x new_script $ ls -l new_script -rwxrwxr-x 1 user user 20 Apr 30 12:12 new_script
Questo comando imposta i permessi di esecuzione per tutti gli utenti. Tieni presente che questo potrebbe comportare un rischio per la sicurezza, ma per il momento rappresenta un livello di permessi accettabile.
$ ./new_script Hello World!
Ora siamo in grado di eseguire il nostro script.
Definire un Interprete
Come abbiamo mostrato è possibile inserire semplicemente del testo in un file, impostarlo come eseguibile ed eseguirlo. new_script
è ancora tecnicamente un normale file di testo, ma siamo riusciti a fare in modo che venisse interpretato da Bash. Ma cosa succederebbe se fosse scritto in Perl o in Python?
È buona norma specificare il tipo di interprete che si desidera utilizzare nella prima riga di uno script. Questa riga è chiamata bang line o più comunemente shebang e indica al sistema come vogliamo eseguire il file. Dato che stiamo imparando Bash, useremo il percorso assoluto del nostro eseguibile Bash, trovandolo ancora una volta tramite which
:
$ which bash /bin/bash
Il nostro shebang inizia con un segno cancelletto e un punto esclamativo, seguito dal percorso assoluto individuato in precedenza. Apriamo new_script
con un editor di testo e inseriamo lo shebang. Cogliamo anche l’occasione per inserire un commento nel nostro script. I commenti vengono ignorati dall’interprete. Sono scritti per gli altri utenti che desiderano comprendere lo script.
#!/bin/bash # This is our first comment. It is also good practice to document all scripts. echo "Hello World!"
Cambiamo anche il nome del file salvandolo come new_script.sh
. Il suffisso .sh
non modifica in alcun modo l’esecuzione del file. È convenzione attribuire agli script bash le estensioni .sh
o .bash
per identificarli più facilmente, proprio come gli script Python vengono solitamente identificati dal suffisso .py
.
Editor di Testo Comuni
Gli utenti Linux spesso devono lavorare in un ambiente in cui non sono disponibili degli editor di testo grafici. Per tale motivo si consiglia vivamente di sviluppare almeno una certa dimestichezza nel modificare i file di testo da Command Line. Due dei più comuni editor di testo sono vi
e nano
.
vi
vi
è un antico editor di testo ed è installato di default su quasi tutti i sistemi Linux esistenti. Da vi
ha avuto origine un clone chiamato vi IMproved o vim
che aggiunge alcune funzionalità, mantenendo però l’interfaccia di vi
. Anche se lavorare con vi
può sembrare difficoltoso per un nuovo utente, questo editor è molto popolare e amato da coloro che ne conoscono le numerose funzionalità.
La differenza più importante tra vi
e applicazioni quali per esempio, il Blocco Note è che vi
ha tre diverse modalità. All’avvio, i tasti H, J, K e L vengono utilizzati per navigare, non per scrivere. In questa modalità di navigazione, puoi premere I per accedere alla modalità di inserimento. A quel punto, puoi scrivere normalmente. Per uscire dalla modalità di inserimento, puoi premere Esc che ti farà tornare alla modalità di navigazione. Dalla modalità di navigazione, puoi premere : per accedere alla modalità di comando. Da questa modalità è possibile salvare, eliminare, uscire o modificare le opzioni.
Sebbene vi
abbia una curva di apprendimento, nel tempo le diverse modalità possono consentire a un utente esperto di diventare più efficiente di quanto sarebbe invece con altri editor.
nano
nano
è uno strumento più recente, progettato per essere semplice e più facile da usare rispetto a vi
. nano
non prevede modalità differenti. Anzi: un utente all’avvio può iniziare a scrivere e può usare Ctrl per accedere agli strumenti mostrati nella parte inferiore dello schermo.
[ Welcome to nano. For basic help, type Ctrl+G. ] ^G Get Help ^O Write Out ^W Where Is ^K Cut Text ^J Justify ^C Cur Pos M-U Undo ^X Exit ^R Read File ^\ Replace ^U Uncut Text ^T To Spell ^_ Go To Line M-E Redo
Gli editor di testo sono solo una questione di preferenze personali e l’editor che scegli di utilizzare non farà alcuna differenza in questa lezione. Acquisire però familiarità e sentirsi a proprio agio con uno o più editor di testo ripagherà certamente in futuro.
Variabili
Le variabili sono una parte importante di qualsiasi linguaggio di programmazione e lo stesso vale anche per Bash. Quando avvii una nuova sessione da terminale, la shell imposta per te alcune variabili. La variabile PATH
ne è un esempio. Queste sono chiamate variabili ambientali, poichè generalmente definiscono le caratteristiche del nostro ambiente di shell. È possibile modificare e aggiungere variabili ambientali, ma per ora ci concentreremo su come impostare le variabili all’interno dei nostri script.
Modifichiamo il nostro script in questo modo:
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username=Carol echo "Hello $username!"
In questo caso abbiamo creato una variabile chiamata username
e le abbiamo assegnato il valore Carol
. Nota che non ci sono spazi tra il nome della variabile, il segno di uguale e il valore assegnato.
Nella riga successiva, abbiamo usato il comando echo
con la variabile, ma con un segno di dollaro ($
) davanti al nome della variabile. Questo è importante, poiché indica alla shell che desideriamo trattare username
come una variabile e non come una normale parola. Inserendo $username
nel nostro comando, indichiamo che vogliamo eseguire una sostituzione, sostituendo il nome della variabile con il valore a essa assegnato.
Eseguendo il nuovo script, otteniamo questo output:
$ ./new_script.sh Hello Carol!
-
I nomi delle variabili devono contenere solo caratteri alfanumerici o underscore (trattini bassi) e fanno distinzione tra lettere maiuscole e minuscole.
Username
eusername
sono trattate come due differenti variabili. -
La sostituzione di variabile può anche seguire il formato
${username}
, con l’aggiunta di{}
. Anche questa forma è accettata. -
Le variabili in Bash hanno un tipo implicito e sono considerate stringhe. Ciò significa che eseguire funzioni matematiche in Bash è più complicato di quanto lo sarebbe in altri linguaggi di programmazione come C/C++:
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username=Carol x=2 y=4 z=$x+$y echo "Hello $username!" echo "$x + $y" echo "$z"
$ ./new_script.sh Hello Carol! 2 + 4 2+4
Uso delle Virgolette con le Variabili
Apportiamo la seguente modifica al valore della nostra variabile username
:
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username=Carol Smith echo "Hello $username!"
L’esecuzione di questo script ci darà un errore:
$ ./new_script.sh ./new_script.sh: line 5: Smith: command not found Hello !
Ricorda che Bash è un interprete e, come tale, interpreta il nostro script riga per riga. In questo caso interpreta correttamente username=Carol
che assegna alla variabile username
il valore Carol
, ma poi interpreta lo spazio come la fine di quell’assegnamento e Smith
come il nome di un comando. Per poter includere lo spazio e il nome Smith
nel valore della nostra variabile, utilizziamo le virgolette doppie ("
) attorno al nome.
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username="Carol Smith" echo "Hello $username!"
$ ./new_script.sh Hello Carol Smith!
Una cosa importante da notare in Bash è che le virgolette doppie e le virgolette singole ('
) si comportano in modo molto diverso. Le virgolette doppie sono considerate deboli, poichè consentono all’interprete di eseguire una sostituzione all’interno di esse. Le virgolette singole sono considerate forti, poichè impediscono qualsiasi sostituzione. Considera il seguente esempio:
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username="Carol Smith" echo "Hello $username!" echo 'Hello $username!'
$ ./new_script.sh Hello Carol Smith! Hello $username!
Nel secondo comando echo
viene impedito all’interprete di sostituire $username
con Carol Smith
; quindi l’output viene scritto in modo letterale.
Argomenti
Hai già familiarità con l’utilizzo degli argomenti nelle principali utility di Linux. Per esempio, rm testfile
contiene sia l’eseguibile rm
che un argomento chiamato testfile
. Gli argomenti possono essere passati a uno script durante l’esecuzione e modificarne il comportamento. Sono facili da implementare.
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username=$1 echo "Hello $username!"
Invece di assegnare un valore alla variabile username
direttamente all’interno dello script, le assegniamo il valore di una nuova variabile $1
, che si riferisce al valore del primo argomento.
$ ./new_script.sh Carol Hello Carol!
I primi nove argomenti vengono gestiti in questo modo. È possibile gestire anche più di nove argomenti, ma questo esula dallo scopo della lezione. Ecco un esempio che usa solo due argomenti:
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username1=$1 username2=$2 echo "Hello $username1 and $username2!"
$ ./new_script.sh Carol Dave Hello Carol and Dave!
C’è una importante considerazione da fare quando si usano gli argomenti: nell’esempio sopra riportato, ci sono due argomenti: Carol
e Dave
, assegnati rispettivamente a $1
e $2
. Se, per esempio, mancasse il secondo argomento, la shell non genererebbe un errore, ma il valore di $2
sarebbe semplicemente null, o nessun valore.
$ ./new_script.sh Carol Hello Carol and !
Nel nostro caso, potrebbe essere una buona idea introdurre un po' di logica all’interno dello script in modo che differenti condizioni influenzino l'output che vogliamo stampare. Inizieremo introducendo un’altra utile variabile e poi passeremo alla creazione di istruzioni if.
Restituire il Numero di Argomenti
Mentre alcune variabili come $1
e $2
contengono il valore degli argomenti posizionali, un’altra variabile, $#
, contiene il numero di argomenti.
#!/bin/bash # This is our first comment. It is also good practice to comment all scripts. username=$1 echo "Hello $username!" echo "Number of arguments: $#."
$ ./new_script.sh Carol Dave Hello Carol! Number of arguments: 2.
Logica Condizionale
Nella programmazione, l’uso della logica condizionale è un argomento molto vasto e non verrà trattato in modo approfondito in questa lezione. Ci concentreremo sulla sintassi delle istruzioni condizionali in Bash, sintassi che differisce dalla maggior parte degli altri linguaggi di programmazione.
Cominciamo esaminando ciò che speriamo di ottenere. Abbiamo un semplice script in grado di stampare un saluto a un singolo utente; se venisse indicato più di un utente, dovrebbe essere stampato un messaggio di errore.
-
La condizione che stiamo testando riguarda il numero di utenti, che è contenuto nella variabile
$#
. Vogliamo sapere se il valore di$#
è1
. -
Se la condizione è vera, l'azione intrapresa sarà salutare l’utente.
-
Se la condizione è falsa, verrà stampato un messaggio di errore.
Ora che la logica è chiara, concentriamoci sulla sintassi necessaria per implementarla.
#!/bin/bash # A simple script to greet a single user. if [ $# -eq 1 ] then username=$1 echo "Hello $username!" else echo "Please enter only one argument." fi echo "Number of arguments: $#."
La logica condizionale è contenuta tra if
e fi
. La condizione da verificare si trova tra la parentesi quadre []
e l’azione da intraprendere se la condizione è vera è indicata dopo then
. Nota gli spazi tra le parentesi quadre e la logica in esse contenuta. Tralasciare questi spazi causerà errori.
Questo script mostrerà come output o il nostro saluto o il messaggio di errore, ma stamperà sempre la riga Number of arguments
.
$ ./new_script.sh Please enter only one argument. Number of arguments: 0. $ ./new_script.sh Carol Hello Carol! Number of arguments: 1.
Nota l’istruzione if
: abbiamo usato -eq
per fare un confronto numerico. In questo caso, stiamo verificando che il valore di $#
sia uguale a uno. Gli altri confronti che possiamo eseguire sono:
-ne
-
Diverso da
-gt
-
Maggiore di
-ge
-
Maggiore o uguale a
-lt
-
Minore di
-le
-
Minore o uguale a
Esercizi Guidati
-
L’utente digita quanto segue nella propria shell:
$ PATH=~/scripts $ ls Command 'ls' is available in '/bin/ls' The command could not be located because '/bin' is not included in the PATH environment variable. ls: command not found
-
Cosa ha fatto l’utente?
-
Quale comando può essere usato per aggiungere la nuova directory
~/scripts
al valore corrente diPATH
?
-
-
Considera il seguente script. Nota che viene usato
elif
per verificare una seconda condizione:> /!bin/bash > fruit1 = Apples > fruit2 = Oranges if [ $1 -lt $# ] then echo "This is like comparing $fruit1 and $fruit2!" > elif [$1 -gt $2 ] then > echo '$fruit1 win!' else > echo "Fruit2 win!" > done
-
Le righe contrassegnate da un
>
contengono degli errori. Correggili.
-
-
Quale sarà l’output in ciascuno dei seguenti casi?
$ ./guided1.sh 3 0
$ ./guided1.sh 2 4
$ ./guided1.sh 0 1
Esercizi Esplorativi
-
Scrivi un semplice script che controlli se vengono passati esattamente due argomenti. In tal caso, stampa gli argomenti in ordine inverso. Considera questo esempio (nota: il tuo codice potrebbe avere un aspetto diverso, ma dovrebbe restituire lo stesso output):
if [ $1 == $number ] then echo "True!" fi
-
Questo codice è corretto ma non rappresenta un confronto numerico. Facendo una ricerca su Internet, scopri in che modo questo codice differisca dall’utilizzo di
-eq
. -
Esiste una variabile ambientale che stampa la directory corrente. Usa
env
per scoprire il nome di questa variabile. -
In base a quanto appreso nelle domande 2 e 3, scrivi un breve script che accetti un argomento. Quando viene passato, controlla se quell’argomento corrisponde al nome della directory corrente. In tal caso, stampa
yes
; altrimenti, stampano
.
Sommario
In questa lezione hai imparato:
-
Come creare ed eseguire semplici script;
-
Come usare uno shebang per specificare un interprete;
-
Come impostare e utilizzare le variabili all’interno degli script;
-
Come gestire gli argomenti negli script;
-
Come costruire istruzioni
if
; -
Come confrontare numeri utilizzando operatori numerici.
Comandi utilizzati negli esercizi:
echo
-
Stampa una stringa sullo standard output.
env
-
Stampa tutte le variabili ambientali sullo standard output.
which
-
Stampa il percorso assoluto di un comando.
chmod
-
Modifica i permessi di un file.
Variabili speciali utilizzate negli esercizi:
$1, $2, … $9
-
Contengono gli argomenti posizionali passati allo script.
$#
-
Contiene il numero di argomenti passati allo script.
$PATH
-
Contiene le directory in cui si trovano gli eseguibili utilizzati dal sistema.
Operatori utilizzati negli esercizi:
-ne
-
Diverso da
-gt
-
Maggiore di
-ge
-
Maggiore o uguale a
-lt
-
Minore di
-le
-
Minore o uguale a
Risposte agli Esercizi Guidati
-
L’utente digita quanto segue nella propria shell:
$ PATH=~/scripts $ ls Command 'ls' is available in '/bin/ls' The command could not be located because '/bin' is not included in the PATH environment variable. ls: command not found
-
Cosa ha fatto l’utente?
L’utente ha sovrascritto il contenuto di PATH con la directory
~/scripts
. Il comandols
non può più essere trovato, poiché non è contenuto in PATH. Nota che questa modifica influisce solo sulla sessione corrente; disconnettiti e accedi nuovamente per annullare la modifica. -
Quale comando può essere usato per aggiungere la nuova directory
~/scripts
al valore corrente diPATH
?PATH=$PATH:~/scripts
-
-
Considera il seguente script. Nota che viene usato
elif
per verificare una seconda condizione:> /!bin/bash > fruit1 = Apples > fruit2 = Oranges if [ $1 -lt $# ] then echo "This is like comparing $fruit1 and $fruit2!" > elif [$1 -gt $2 ] then > echo '$fruit1 win!' else > echo "Fruit2 win!" > done
-
Le righe contrassegnate da un
>
contengono degli errori. Correggili.#!/bin/bash fruit1=Apples fruit2=Oranges if [ $1 -lt $# ] then echo "This is like comparing $fruit1 and $fruit2!" elif [ $1 -gt $2 ] then echo "$fruit1 win!" else echo "$fruit2 win!" fi
-
-
Quale sarà l’output in ciascuno dei seguenti casi?
$ ./guided1.sh 3 0
Apples win!
$ ./guided1.sh 2 4
Oranges win!
$ ./guided1.sh 0 1
This is like comparing Apples and Oranges!
Risposte agli Esercizi Esplorativi
-
Scrivi un semplice script che controlli se vengono passati esattamente due argomenti. In tal caso, stampa gli argomenti in ordine inverso. Considera questo esempio (nota: il tuo codice potrebbe avere un aspetto diverso, ma dovrebbe restituire lo stesso output):
if [ $1 == $number ] then echo "True!" fi
#!/bin/bash if [ $# -ne 2 ] then echo "Error" else echo "$2 $1" fi
-
Questo codice è corretto ma non rappresenta un confronto numerico. Facendo una ricerca su Internet, scopri in che modo questo codice differisca dall’utilizzo di
-eq
.Con
==
si confrontano stringhe, ovvero: se i caratteri di entrambe le variabili corrispondono esattamente, la condizione è vera.abc == abc
vero
abc == ABC
falso
1 == 1
vero
1+1 == 2
falso
I confronti tra stringhe causano un comportamento anomalo se usati in condizioni di test con numeri.
-
Esiste una variabile ambientale che stampa la directory corrente. Usa
env
per scoprire il nome di questa variabile.PWD
-
In base a quanto appreso nelle domande 2 e 3, scrivi un breve script che accetti un argomento. Quando viene passato, controlla se quell’argomento corrisponde al nome della directory corrente. In tal caso, stampa
yes
; altrimenti, stampano
.#!/bin/bash if [ "$1" == "$PWD" ] then echo "yes" else echo "no" fi