Una shell è l'interprete dei comandi per il sistema operativo. Bash è la mia shell preferita, ma ogni shell Linux interpreta i comandi digitati dall'utente o dall'amministratore di sistema in una forma che il sistema operativo può utilizzare. Quando i risultati vengono restituiti al programma shell, li invia a STDOUT che, per impostazione predefinita, li visualizza nel terminale. Tutte le shell che conosco sono anche linguaggi di programmazione.
Funzionalità come il completamento delle schede, 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 modifica predefinita dalla riga di comando utilizza Emacs, ma una delle mie funzionalità Bash preferite è che posso cambiarla in modalità Vi per utilizzare i comandi di modifica che fanno già parte della mia memoria muscolare.
Tuttavia, se pensi a Bash esclusivamente come a un guscio, perdi gran parte del suo vero potere. Durante la ricerca del mio corso di autoapprendimento su 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. Alcuni di questi nuovi bit di conoscenza si riferiscono al suo utilizzo come linguaggio di programmazione. Bash è un potente linguaggio di programmazione, perfettamente progettato per l'uso sulla riga di comando e negli script di shell.
Questa serie in tre parti esplora l'utilizzo di Bash come linguaggio di programmazione dell'interfaccia della riga di comando (CLI). Questo primo articolo esamina una semplice programmazione da riga di comando con Bash, variabili e operatori di controllo. Gli altri articoli esplorano i tipi di file Bash; operatori logici stringa, numerici e vari che forniscono logica di controllo del flusso di esecuzione; diversi tipi di espansioni della shell; e il per , mentre e fino a loop che consentono operazioni ripetitive. Esamineranno anche alcuni comandi che semplificano e supportano l'uso di questi strumenti.
La shell
Una shell è l'interprete dei comandi per il sistema operativo. Bash è la mia shell preferita, ma ogni shell Linux interpreta i comandi digitati dall'utente o dall'amministratore di sistema in una forma che il sistema operativo può utilizzare. Quando i risultati vengono restituiti al programma shell, li visualizza nel terminale. Tutte le shell che conosco sono anche linguaggi di programmazione.
Bash sta per Bourne Again Shell perché la shell Bash è basata sulla vecchia Bourne shell 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 a cui piace 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 funzionalità più facili da usare
- zsh: La shell Z, che combina molte caratteristiche di altre shell popolari
Tutte le shell hanno comandi integrati che integrano o sostituiscono quelli forniti dalle utility principali. Apri la pagina man della shell e trova la sezione "BUILT-INS" per vedere i comandi che fornisce.
Ogni shell ha la sua personalità e sintassi. Alcuni funzioneranno meglio per te rispetto ad altri. Ho usato la shell C, la shell Korn e la shell Z. Mi piace ancora la shell Bash più di chiunque altro. Usa quello che funziona meglio per te, anche se ciò potrebbe richiedere di provare alcuni degli altri. 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 parti integranti di Bash.
Bash come linguaggio di programmazione
La maggior parte degli amministratori di sistema ha utilizzato Bash per emettere comandi che di solito sono abbastanza semplici e diretti. Ma Bash può andare oltre l'immissione di singoli comandi e molti amministratori di sistema creano semplici programmi da riga di comando per eseguire una serie di attività. Questi programmi sono strumenti comuni che possono far risparmiare tempo e fatica.
Il mio obiettivo quando scrivo programmi CLI è risparmiare tempo e fatica (cioè essere l'amministratore di sistema pigro). I programmi CLI supportano questo elencando diversi comandi in una sequenza specifica che vengono eseguiti uno dopo l'altro, quindi non è necessario guardare l'avanzamento di un comando e digitare il comando successivo al termine del primo. Puoi andare a fare altre cose e non dover monitorare continuamente l'avanzamento di ogni comando.
Cos'è "un programma"?
Il Free Online Dictionary of Computing (FOLDOC) definisce un programma come:"Le istruzioni eseguite da un computer, in contrasto con il dispositivo fisico su cui vengono eseguite". 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.
Pertanto, un programma può essere costituito da una o più istruzioni che eseguono un'attività specifica e correlata. Un'istruzione di programma per computer è anche chiamata istruzione di programma. Per gli amministratori di sistema, un programma è solitamente una sequenza di comandi della shell. Tutte le shell disponibili per Linux, almeno quelle che conosco, hanno almeno una forma base di capacità di programmazione e Bash, la shell predefinita per la maggior parte delle distribuzioni Linux, non fa eccezione.
Sebbene questa serie utilizzi Bash (perché è così onnipresente), se usi una shell diversa, i concetti generali di programmazione saranno gli stessi, sebbene i costrutti e la sintassi possano differire in qualche modo. Alcune shell possono supportare alcune funzionalità che altre no, ma tutte forniscono alcune capacità di programmazione. I programmi della shell possono essere archiviati in un file per un uso ripetuto, oppure possono essere creati sulla riga di comando secondo necessità.
Programmi CLI semplici
I programmi da riga di comando più semplici sono una o due istruzioni di programma consecutive, che possono essere correlate o meno, che vengono immesse sulla riga di comando prima di Invio viene premuto il tasto. La seconda affermazione in un programma, se presente, potrebbe dipendere dalle azioni della prima, ma non è necessario.
C'è anche un po' di punteggiatura sintattica che deve essere chiaramente indicata. Quando si immette un singolo comando sulla riga di comando, premendo il tasto Invio key termina il comando con un punto e virgola implicito (; ). Quando viene utilizzato in un programma shell CLI immesso come riga singola sulla riga di comando, il punto e virgola deve essere utilizzato per terminare ogni istruzione e separarla dalla successiva. L'ultima istruzione in un programma shell CLI può utilizzare un punto e virgola esplicito o implicito.
Alcune sintassi di base
I seguenti esempi chiariranno questa sintassi. Questo programma consiste in un singolo comando con un terminatore esplicito:
[student@studentvm1 ~]$ echo "Hello world." ;
Hello world.
Potrebbe non sembrare un granché, ma è il primo programma che incontro con ogni nuovo linguaggio di programmazione che imparo. La sintassi potrebbe essere leggermente diversa per ogni lingua, ma il risultato è lo stesso.
Espandiamo un po' questo programma banale ma onnipresente. I tuoi risultati saranno diversi dai miei perché ho fatto altri esperimenti, mentre potresti avere solo le directory e i file predefiniti che vengono creati nella home directory dell'account la prima volta che accedi a un account tramite il desktop della GUI.
[student@studentvm1 ~]$ 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 dichiarazioni del programma sono indipendenti l'una dall'altra. Si noti che mi piace mettere gli 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:
[student@studentvm1 ~]$ echo "My home directory." ; ls
Non ci sono differenze nell'output.
Qualcosa sulle variabili
Come tutti i linguaggi di programmazione, la shell Bash può gestire variabili. Una variabile è un nome simbolico che fa riferimento a una posizione specifica nella memoria che contiene un valore di qualche tipo. Il valore di una variabile è modificabile, cioè è variabile.
Bash non digita variabili come C e linguaggi correlati, definendole come numeri interi, virgola mobile o tipi di stringa. In Bash, tutte le variabili sono stringhe. Una stringa che è un numero intero può essere utilizzata nell'aritmetica degli interi, che è l'unico tipo di matematica che Bash è in grado di fare. Se è richiesta una matematica più complessa, il bc comando può essere utilizzato in programmi e script CLI.
Alle variabili vengono assegnati valori e possono essere utilizzate per fare riferimento a tali valori nei programmi e negli script CLI. Il valore di una variabile viene impostato utilizzando il suo nome ma non preceduto da $ cartello. L'assegnazione VAR=10 imposta il valore della variabile VAR a 10. Per stampare il valore della variabile, puoi usare l'istruzione echo $VAR . Inizia con variabili di testo (cioè non numeriche).
Le variabili Bash diventano parte dell'ambiente della shell finché non vengono annullate.
Verificare il valore iniziale di una variabile che non è stata assegnata; dovrebbe essere nullo. Quindi assegna un valore alla variabile e stampala per verificarne il valore. Puoi fare tutto questo in un unico programma CLI:
[student@studentvm1 ~]$ echo $MyVar ; MyVar="Hello World" ; echo $MyVar ;
Hello World
[student@studentvm1 ~]$
Nota:la sintassi dell'assegnazione delle variabili è molto rigida. Non devono esserci spazi su entrambi i lati dell'uguale (= ) firmare la dichiarazione di assegnazione.
La riga vuota indica che il valore iniziale di MyVar è zero. La modifica e l'impostazione del valore di una variabile vengono eseguite allo stesso modo. Questo esempio mostra sia il valore originale che quello nuovo.
Come accennato, Bash può eseguire calcoli aritmetici interi, utili per calcolare un riferimento alla posizione di un elemento in un array o per eseguire semplici problemi di matematica. Non è adatto per l'informatica scientifica o qualsiasi cosa che richieda decimali, come i calcoli finanziari. Esistono strumenti molto migliori per questi tipi di calcoli.
Ecco un semplice calcolo:
[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = $((Var1*Var2))"
Result = 63
Cosa succede quando esegui un'operazione matematica che risulta in un numero a virgola mobile?
[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = $((Var1/Var2))"
Result = 0
[student@studentvm1 ~]$ Var1="7" ; Var2="9" ; echo "Result = $((Var2/Var1))"
Result = 1
[student@studentvm1 ~]$
Il risultato è l'intero più vicino. Nota che il calcolo è stato eseguito come parte dell'eco dichiarazione. La matematica viene eseguita prima del comando echo di inclusione a causa dell'ordine di precedenza di Bash. Per i dettagli, vedere la pagina man di Bash e cercare "precedenza".
Operatori di controllo
Gli operatori di controllo della shell sono uno degli operatori sintattici per creare facilmente alcuni interessanti programmi da riga di comando. La forma più semplice del programma CLI è semplicemente mettere insieme diversi comandi in una sequenza sulla riga di comando:
command1 ; command2 ; command3 ; command4 ; . . . ; etc. ;
Questi comandi vengono eseguiti tutti senza problemi fintanto che non si verificano errori. Ma cosa succede quando si verifica un errore? Puoi anticipare e consentire errori utilizzando && integrato e || Operatori di controllo Bash. Questi due operatori di controllo forniscono alcuni controlli di flusso e consentono di modificare la sequenza di esecuzione del codice. Il punto e virgola è anche considerato un operatore di controllo Bash, così come il carattere di nuova riga.
Il && l'operatore dice semplicemente "se comando1 ha esito positivo, esegui comando2. Se comando1 non riesce per qualsiasi motivo, comando2 viene saltato". Quella sintassi è simile a questa:
command1 && command2
Ora, guarda alcuni comandi che creeranno una nuova directory e, se ha esito positivo, la trasformeranno nella directory di lavoro attuale (PWD). Assicurati che la tua home directory (~ ) è la PWD. Provalo prima in /root , una directory a cui non hai accesso:
[student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir/ && cd $Dir
mkdir: cannot create directory '/root/testdir/': Permission denied
[student@studentvm1 ~]$
L'errore è stato emesso da mkdir comando. Non hai ricevuto un messaggio di errore che indica che non è stato possibile creare il file perché la creazione della directory non è riuscita. Il && l'operatore di controllo ha rilevato il codice di ritorno diverso da zero, quindi il tocco il comando è stato saltato. Usando && l'operatore di controllo impedisce il tocco comando dall'esecuzione perché si è verificato un errore durante la creazione della directory. Questo tipo di controllo del flusso del programma da riga di comando può impedire che gli errori si accumulino e creino un vero pasticcio. Ma è ora di complicarsi un po'.
Il || operatore di controllo consente 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 è simile alla seguente:
command1 || command2
Questa sintassi recita "Se comando1 non riesce, esegui comando2". Ciò implica che se comando1 ha esito positivo, comando2 viene saltato. Prova questo tentando di creare una nuova directory:
[student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir || echo "$Dir was not created."
mkdir: cannot create directory '/root/testdir': Permission denied
/root/testdir was not created.
[student@studentvm1 ~]$
Questo è esattamente quello che ti aspetteresti. Poiché non è stato possibile creare la nuova directory, il primo comando non è riuscito, provocando l'esecuzione del secondo comando.
La combinazione di questi due operatori offre il meglio di entrambi. La sintassi dell'operatore di controllo che utilizza alcuni controlli di flusso assume questa forma generale quando && e || vengono utilizzati gli operatori di controllo:
preceding commands ; command1 && command2 || command3 ; following commands
Questa sintassi può essere espressa in questo modo:"Se comando1 esce con un codice di ritorno pari a 0, esegui comando2, altrimenti esegui comando3". Provalo:
[student@studentvm1 ~]$ Dir=/root/testdir ; mkdir $Dir && cd $Dir || echo "$Dir was not created."
mkdir: cannot create directory '/root/testdir': Permission denied
/root/testdir was not created.
[student@studentvm1 ~]$
Ora prova di nuovo l'ultimo comando usando la tua home directory invece di /root directory. Avrai l'autorizzazione per creare questa directory:
[student@studentvm1 ~]$ Dir=~/testdir ; mkdir $Dir && cd $Dir || echo "$Dir was not created."
[student@studentvm1 testdir]$
La sintassi dell'operatore di controllo, come comando1 &&comando2 , funziona perché ogni comando invia un codice di ritorno (RC) alla shell che indica se è stato completato correttamente o se si è verificato un qualche tipo di errore durante l'esecuzione. Per convenzione, un RC di zero (0) indica successo e qualsiasi numero positivo indica un tipo di errore. Alcuni degli strumenti utilizzati dagli amministratori di sistema restituiscono semplicemente uno (1) per indicare un errore, ma molti usano altri codici per indicare il tipo di errore che si è verificato.
La variabile della shell Bash $? contiene l'RC dell'ultimo comando. Questo RC può essere controllato molto facilmente da uno script, dal comando successivo in un elenco di comandi o anche direttamente dall'amministratore di sistema. Inizia eseguendo un semplice comando e controllando immediatamente l'RC. L'RC sarà sempre per l'ultimo comando eseguito prima che tu lo guardassi.
[student@studentvm1 testdir]$ ll ; echo "RC = $?"
total 1264
drwxrwxr-x 2 student student 4096 Mar 2 08:21 chapter25
drwxrwxr-x 2 student student 4096 Mar 21 15:27 chapter26
-rwxr-xr-x 1 student student 92 Mar 20 15:53 TestFile1
<snip>
drwxrwxr-x. 2 student student 663552 Feb 21 14:12 testdir
drwxr-xr-x. 2 student student 4096 Dec 22 13:15 Videos
RC = 0
[student@studentvm1 testdir]$
L'RC, in questo caso, è zero, il che significa che il comando è stato completato con successo. Ora prova lo stesso comando sulla home directory di root, una directory per la quale non hai i permessi:
[student@studentvm1 testdir]$ ll /root ; echo "RC = $?"
ls: cannot open directory '/root': Permission denied
RC = 2
[student@studentvm1 testdir]$
In questo caso, il RC è due; ciò significa che l'autorizzazione è stata negata a un utente non root per accedere a una directory a cui l'utente non ha accesso. Gli operatori di controllo utilizzano questi RC per consentire di modificare la sequenza di esecuzione del programma.
Riepilogo
Questo articolo ha esaminato Bash come linguaggio di programmazione e ne ha esplorato la sintassi di base, nonché alcuni strumenti di base. Ha mostrato come stampare i dati su STDOUT e come utilizzare variabili e operatori di controllo. Il prossimo articolo di questa serie esamina alcuni dei molti operatori logici Bash che controllano il flusso di esecuzione delle istruzioni.