Una shell è l’interprete di comandi per il sistema operativo. Bash è la mia shell preferita, ma ogni shell Linux interpreta i comandi digitati dall’utente o dal sysadmin in una forma che il sistema operativo può utilizzare. Quando i risultati vengono restituiti al programma della shell, questo li invia a STDOUT che, per impostazione predefinita, li visualizza nel terminale. Tutte le shell con cui ho familiarità sono anche linguaggi di programmazione.

Caratteristiche come il completamento della scheda, il richiamo e la modifica della riga di comando e le scorciatoie come gli alias contribuiscono al suo valore come potente shell. La sua modalità di editing a riga di comando predefinita utilizza Emacs, ma una delle mie caratteristiche preferite di Bash è che posso cambiarla in modalità Vi per utilizzare i comandi di editing che sono già parte della mia memoria muscolare.

Tuttavia, se si pensa a Bash solo come una shell, si perde molto del suo vero potere. Durante la ricerca del mio corso di autoapprendimento di Linux in tre volumi (su cui si basa questa serie di articoli), ho imparato cose su Bash che non avevo mai saputo in oltre 20 anni di lavoro con Linux. Alcune di queste nuove conoscenze riguardano il suo uso come linguaggio di programmazione. Bash è un potente linguaggio di programmazione, perfettamente progettato per l’uso sulla linea di comando e negli script di shell.

Questa serie in tre parti esplora l’uso di Bash come linguaggio di programmazione dell’interfaccia a riga di comando (CLI). Questo primo articolo esamina alcuni semplici programmi a riga di comando con Bash, variabili e operatori di controllo. Gli altri articoli esplorano i tipi di file Bash; gli operatori logici di stringa, numerici e vari che forniscono una logica di controllo del flusso di esecuzione; diversi tipi di espansioni di shell; e i cicli for, while e until che permettono operazioni ripetitive. Vedranno anche alcuni comandi che semplificano e supportano l’uso di questi strumenti.

La shell

Una shell è l’interprete di comandi per il sistema operativo. Bash è la mia shell preferita, ma ogni shell Linux interpreta i comandi digitati dall’utente o dal sysadmin in una forma che il sistema operativo può utilizzare. Quando i risultati vengono restituiti al programma della shell, questo li visualizza nel terminale. Tutte le shell con cui ho familiarità sono anche linguaggi di programmazione.

Bash sta per Bourne Again Shell perché la shell Bash è basata sulla vecchia shell Bourne che fu scritta da Steven Bourne nel 1977. Sono disponibili molte altre shell, ma queste sono le quattro che incontro più frequentemente:

  • csh: La shell C per i programmatori che amano la sintassi del linguaggio C
  • ksh: La shell Korn, scritta da David Korn e popolare tra gli utenti Unix
  • tcsh: Una versione di csh con caratteristiche più facili da usare
  • zsh: La shell Z, che combina molte caratteristiche di altre popolari shell

Tutte le shell hanno comandi integrati che integrano o sostituiscono quelli forniti dalle utility di base. Aprite la pagina man della shell e trovate la sezione “BUILT-INS” per vedere i comandi che fornisce.

Ogni shell ha la propria personalità e sintassi. Alcune funzioneranno meglio di altre. Ho usato la shell C, la shell Korn e la shell Z. Mi piace ancora la shell Bash più di tutte le altre. Usate quella che funziona meglio per voi, anche se questo potrebbe richiedere di provare alcune delle altre. Fortunatamente, è abbastanza facile cambiare shell.

Tutte queste shell sono linguaggi di programmazione e interpreti di comandi. Ecco un rapido tour di alcuni costrutti e strumenti di programmazione che sono parte integrante di Bash.

Bash come linguaggio di programmazione

La maggior parte dei sysadmin ha usato Bash per impartire comandi che sono solitamente abbastanza semplici e diretti. Ma Bash può andare oltre l’inserimento di singoli comandi, e molti sysadmin creano semplici programmi a riga di comando per eseguire una serie di compiti. Questi programmi sono strumenti comuni che possono far risparmiare tempo e fatica.

Il mio obiettivo quando scrivo programmi CLI è quello di risparmiare tempo e fatica (cioè, essere il sysadmin pigro). I programmi CLI supportano ciò elencando diversi comandi in una sequenza specifica che vengono eseguiti uno dopo l’altro, così non è necessario guardare il progresso di un comando e digitare il comando successivo quando il primo finisce. Potete andare a fare altre cose senza dover monitorare continuamente il progresso di ogni comando.

Cos’è “un programma”?

Il Free On-line Dictionary of Computing (FOLDOC) definisce un programma come: “Le istruzioni eseguite da un computer, in opposizione al dispositivo fisico su cui vengono eseguite”. Il WordNet dell’Università di Princeton definisce un programma come: “…una sequenza di istruzioni che un computer può interpretare ed eseguire…”. Wikipedia ha anche una buona voce sui programmi per computer.

Quindi, un programma può consistere in una o più istruzioni che eseguono un compito specifico e correlato. Un’istruzione di un programma per computer è anche chiamata dichiarazione di programma. Per i sysadmin, un programma è di solito una sequenza di comandi di shell. Tutte le shell disponibili per Linux, almeno quelle con cui ho familiarità, hanno almeno una forma basilare di capacità di programmazione, e Bash, la shell predefinita per la maggior parte delle distribuzioni Linux, non fa eccezione.

Mentre questa serie usa Bash (perché è così onnipresente), se si usa una shell diversa, i concetti generali di programmazione saranno gli stessi, sebbene i costrutti e la sintassi possano differire un po’. Alcune shell possono supportare alcune caratteristiche che altre non supportano, ma tutte forniscono qualche capacità di programmazione. I programmi della shell possono essere memorizzati in un file per un uso ripetuto, o possono essere creati sulla linea di comando quando necessario.

Programmi CLI semplici

I programmi da linea di comando più semplici sono una o due istruzioni di programma consecutive, che possono essere correlate o meno, che vengono inserite sulla linea di comando prima che venga premuto il tasto Invio. La seconda affermazione in un programma, se ce n’è una, potrebbe dipendere dalle azioni della prima, ma non è necessario che lo sia.

C’è anche un po’ di punteggiatura sintattica che deve essere chiaramente indicata. Quando si inserisce un singolo comando sulla linea di comando, premendo il tasto Invio si termina il comando con un implicito punto e virgola (;). Quando viene usato in un programma CLI shell inserito come una singola riga sulla linea di comando, il punto e virgola deve essere usato per terminare ogni dichiarazione e separarla da quella successiva. L’ultima affermazione in un programma CLI shell può usare un punto e virgola esplicito o implicito.

Alcune sintassi di base

Gli esempi seguenti chiariranno questa sintassi. Questo programma consiste in un singolo comando con un terminatore esplicito:

$ echo "Hello world." ;
Hello world.

Questo può non sembrare un gran programma, ma è il primo programma che incontro con ogni nuovo linguaggio di programmazione che imparo. La sintassi può essere un po’ diversa per ogni linguaggio, ma il risultato è lo stesso.

Espandiamo un po’ questo programma banale ma onnipresente. I tuoi risultati saranno diversi dai miei perché io ho fatto altri esperimenti, mentre tu potresti avere solo le directory e i file di default che vengono creati nella directory home dell’account la prima volta che accedi a un account tramite il desktop GUI.

$ echo "My home directory." ; ls ;
My home directory.
chapter25 TestFile1.Linux dmesg2.txt Downloads newfile.txt softlink1 testdir6
chapter26 TestFile1.mac dmesg3.txt file005 Pictures Templates testdir
TestFile1 Desktop dmesg.txt link3 Public testdir Videos
TestFile1.dos dmesg1.txt Documents Music random.txt testdir1

Questo ha un po’ più senso. I risultati sono correlati, ma le singole istruzioni del programma sono indipendenti l’una dall’altra. Notate che mi piace mettere degli spazi prima e dopo il punto e virgola perché rende il codice un po’ più facile da leggere. Prova di nuovo quel piccolo programma CLI senza un punto e virgola esplicito alla fine:

$ echo "My home directory." ; ls 

Non c’è differenza nell’output.

Qualcosa sulle variabili

Come tutti i linguaggi di programmazione, la shell Bash può gestire le variabili. Una variabile è un nome simbolico che si riferisce ad una specifica posizione in memoria che contiene un valore di qualche tipo. Il valore di una variabile è modificabile, cioè, è variabile.

Bash non tipizza le variabili come il C e i linguaggi correlati, definendole come interi, punti fluttuanti o tipi di stringa. In Bash, tutte le variabili sono stringhe. Una stringa che è un intero può essere usata nell’aritmetica degli interi, che è l’unico tipo di matematica che Bash è in grado di fare. Se è richiesta una matematica più complessa, il comando bc può essere usato nei programmi e negli script CLI.

Alle variabili vengono assegnati dei valori e possono essere usati per riferirsi a quei valori nei programmi e negli script CLI. Il valore di una variabile viene impostato usando il suo nome ma non preceduto dal segno $. L’assegnazione VAR=10 imposta il valore della variabile VAR a 10. Per stampare il valore della variabile, potete usare l’istruzione echo $VAR. Iniziate con le variabili di testo (cioè non numeriche).

Le variabili di Bash diventano parte dell’ambiente della shell finché non vengono deselezionate.

Controllate il valore iniziale di una variabile che non è stata assegnata; dovrebbe essere nullo. Poi assegnate un valore alla variabile e stampatelo per verificare il suo valore. Puoi fare tutto questo in un singolo programma CLI:

$ echo $MyVar ; MyVar="Hello World" ; echo $MyVar ;
Hello World
$

Nota: La sintassi dell’assegnazione delle variabili è molto rigorosa. Non ci devono essere spazi su entrambi i lati del segno uguale (=) nella dichiarazione di assegnazione.

La linea vuota indica che il valore iniziale di MyVar è nullo. La modifica e l’impostazione del valore di una variabile sono fatte allo stesso modo. Questo esempio mostra sia il valore originale che quello nuovo.

Come menzionato, Bash può eseguire calcoli aritmetici interi, il che è utile per calcolare un riferimento alla posizione di un elemento in una matrice o per fare semplici problemi matematici. Non è adatto al calcolo scientifico o a qualcosa che richiede decimali, come i calcoli finanziari. Ci sono strumenti molto migliori per questi tipi di calcoli.

Ecco un semplice calcolo:

$ Var1="7" ; Var2="9" ; echo "Result = $((Var1*Var2))"
Result = 63

Cosa succede quando si esegue un’operazione matematica che dà come risultato un numero in virgola mobile?

Il risultato è l’intero più vicino. Notate che il calcolo è stato eseguito come parte dell’istruzione echo. Il calcolo viene eseguito prima del comando echo che lo racchiude, a causa dell’ordine di precedenza di Bash. Per i dettagli vedere la pagina man di Bash e cercare “precedence.”

Operatori di controllo

Gli operatori di controllo della shell sono uno degli operatori sintattici per creare facilmente alcuni interessanti programmi a riga di comando. La forma più semplice di programma CLI è semplicemente stringere diversi comandi insieme in una sequenza sulla linea di comando:

command1 ; command2 ; command3 ; command4 ; . . . ; etc. ;

Questi comandi funzionano tutti senza problemi finché non si verificano errori. Ma cosa succede quando si verifica un errore? Puoi anticipare e permettere gli errori usando gli operatori di controllo integrati && e || Bash. Questi due operatori di controllo forniscono un certo controllo del flusso e permettono di alterare la sequenza di esecuzione del codice. Anche il punto e virgola è considerato un operatore di controllo Bash, così come il carattere newline.

L’operatore && dice semplicemente, “se il comando1 ha successo, allora esegui il comando2. Se il comando1 fallisce per qualsiasi motivo, allora il comando2 viene saltato”. Questa sintassi assomiglia a questa:

command1 && command2

Ora, guardate alcuni comandi che creeranno una nuova directory e – se ha successo – la renderanno la directory di lavoro attuale (PWD). Assicuratevi che la vostra home directory (~) sia la PWD. Provate prima in /root, una directory a cui non avete accesso:

L’errore è stato emesso dal comando mkdir. Non hai ricevuto un errore che indica che il file non può essere creato perché la creazione della directory è fallita. L’operatore di controllo && ha percepito il codice di ritorno non nullo, quindi il comando touch è stato saltato. Usando l’operatore di controllo && si impedisce l’esecuzione del comando touch perché c’è stato un errore nella creazione della directory. Questo tipo di controllo del flusso del programma a riga di comando può evitare che gli errori si sommino e facciano un vero casino. Ma è ora di diventare un po’ più complicato.

L’operatore di controllo || permette di aggiungere un’altra istruzione di programma che viene eseguita quando l’istruzione di programma iniziale restituisce un codice maggiore di zero. La sintassi di base assomiglia a questa:

command1 || command2 

Questa sintassi dice: “Se il comando1 fallisce, esegui il comando2”. Ciò implica che se il comando1 ha successo, il comando2 viene saltato. Provate questo tentando di creare una nuova directory:

Questo è esattamente quello che vi aspettereste. Poiché la nuova directory non può essere creata, il primo comando è fallito, il che ha portato all’esecuzione del secondo comando.

La combinazione di questi due operatori fornisce il meglio di entrambi. La sintassi dell’operatore di controllo che usa un certo controllo di flusso assume questa forma generale quando vengono usati gli operatori di controllo && e ||:

preceding commands ; command1 && command2 || command3 ; following commands

Questa sintassi può essere dichiarata così: “Se il comando1 esce con un codice di ritorno di 0, allora esegui il comando2, altrimenti esegui il comando3”. Provate:

Ora riprovate l’ultimo comando usando la vostra home directory invece della directory /root. Avrete il permesso di creare questa directory:

$ Dir=~/testdir ; mkdir $Dir && cd $Dir || echo "$Dir was not created."
$

La sintassi dell’operatore di controllo, come command1 && command2, funziona perché ogni comando invia un codice di ritorno (RC) alla shell che indica se ha completato con successo o se c’è stato qualche tipo di fallimento durante l’esecuzione. Per convenzione, un RC di zero (0) indica il successo, e qualsiasi numero positivo indica un qualche tipo di fallimento. Alcuni degli strumenti che i sysadmin usano restituiscono semplicemente un uno (1) per indicare un fallimento, ma molti usano altri codici per indicare il tipo di fallimento che si è verificato.

La variabile $? della shell Bash contiene il RC dell’ultimo comando. Questo RC può essere controllato molto facilmente da uno script, dal comando successivo in una lista di comandi, o anche direttamente dal sysadmin. Iniziate eseguendo un semplice comando e controllate immediatamente l’RC. L’RC sarà sempre per l’ultimo comando eseguito prima che voi lo guardiate.

L’RC, in questo caso, è zero, il che significa che il comando è stato completato con successo. Ora provate lo stesso comando sulla home directory di root, una directory per la quale non avete i permessi:

In questo caso, l’RC è due; ciò significa che è stato negato il permesso ad un utente non-root di accedere ad una directory alla quale l’utente non è autorizzato ad accedere. Gli operatori di controllo usano questi RC per permettervi di alterare la sequenza di esecuzione del programma.

Sommario

Questo articolo ha esaminato Bash come linguaggio di programmazione e ha esplorato la sua sintassi di base, nonché alcuni strumenti di base. Ha mostrato come stampare dati su STDOUT e come usare variabili e operatori di controllo. Il prossimo articolo di questa serie esamina alcuni dei molti operatori logici di Bash che controllano il flusso di esecuzione delle istruzioni.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.