I fondamenti di Bash Scripting

Gli script di shell sono ampiamente utilizzati nel mondo UNIX. Sono eccellenti per velocizzare le attività ripetitive e semplificare la complessa logica di esecuzione. Possono essere semplici come un insieme di comandi o possono orchestrare compiti complessi. In questo tutorial, impareremo di più sul linguaggio di scripting di Bash scrivendo uno script di esempio passo dopo passo.


Il problema Fizz-Buzz

Uno dei modi migliori per conoscere una nuova lingua è con l'esempio. Iniziamo con uno.

Il problema di Fizz-Buzz è molto semplice. È diventato famoso dopo che un programmatore, di nome Imran, l'ha usato come test di intervista. Risulta che il 90-99,5% dei candidati per un lavoro di programmazione non è semplicemente in grado di scrivere il programma più semplice. Imran ha preso questo semplice gioco Fizz-Buzz e ha chiesto ai candidati di risolverlo. Molti hanno seguito l'esempio di Imran e, oggi, è una delle domande più frequenti per un lavoro di programmazione. Se stai assumendo e hai bisogno di un modo per filtrare il 90% dei candidati, questo è un grosso problema da presentare.

Ecco le regole:

  • Prendi e stampa i numeri tra 1 e 100.
  • Quando un numero è divisibile per 3, stampa "Fizz" invece del numero.
  • Quando è divisibile per 5, stampa invece "Buzz".
  • Quando è divisibile sia per 3 che per 5, stampa "FizzBuzz".

Questo è tutto ciò che c'è da fare. Sono sicuro che molti di voi possono già visualizzare i due o tre Se dichiarazioni per risolvere questo. Lavoriamo attraverso questo usando il linguaggio di scripting di Bash.


faccenda

Uno shebang si riferisce alla combinazione dei caratteri hash e del punto esclamativo: #!. Il programma di caricamento del programma cercherà uno shebang sulla prima riga dello script e utilizzerà l'interprete specificato al suo interno. Uno shebang è costituito dalla seguente sintassi: #!interprete [parametri]. L'interprete è il programma che viene utilizzato per interpretare la nostra lingua. Per lo scripting bash, sarebbe / Bin / bash. Ad esempio, se si desidera creare uno script in PHP ed eseguirlo in console, probabilmente si vorrebbe usare / Usr / bin / php (o il percorso dell'eseguibile PHP sulla tua macchina) come interprete.

#! / Usr / bin / php  

Sì, funzionerà davvero! Non è semplice? Assicurati di rendere il file eseguibile prima. Una volta fatto, questo script mostrerà le informazioni PHP come ti aspetteresti.

Mancia: Per assicurarti che lo script funzioni su quanti più sistemi possibili, puoi utilizzare / Bin / ENV nello shebang. In quanto tale, invece di / Bin / bash, potresti usare / bin / env bash, che funzionerà su sistemi in cui l'eseguibile bash non è all'interno /bidone.


Uscita di testo

L'output di uno script sarà uguale a, come ci si potrebbe aspettare, qualsiasi cosa venga emessa dal comando. Tuttavia, se vogliamo esplicitamente scrivere qualcosa sullo schermo, possiamo usarlo eco.

#! / bin / bash echo "Hello World"

L'esecuzione di questo script stamperà "Hello World" nella console.

csaba @ csaba ~ $ ./helloWorld.sh Ciao mondo csaba @ csaba ~ $

Presentazione delle variabili

Come con qualsiasi linguaggio di programmazione, quando si scrivono script di shell, è possibile utilizzare variabili.

#! / bin / bash message = "Ciao mondo" echo $ messaggio

Questo codice produce esattamente lo stesso messaggio "Hello World". Come puoi vedere, per assegnare un valore a una variabile, scrivi semplicemente il suo nome - escludi il simbolo del dollaro di fronte ad essa. Inoltre, fai attenzione agli spazi; non possono esserci spazi tra il nome della variabile e il segno di uguale. Così messaggio = "Ciao" invece di messaggio = 'Ciao'

Quando desideri utilizzare una variabile, puoi prenderne il valore proprio come abbiamo fatto noi eco comando. Prepending a $ al nome della variabile restituirà il suo valore.

Mancia: Il punto e virgola non è richiesto nello scripting bash. Puoi usarli nella maggior parte dei casi, ma fai attenzione: potrebbero avere un significato diverso da quello che ti aspetti.


Stampa dei numeri tra 1 e 100

Continuando con il nostro progetto dimostrativo, abbiamo bisogno di scorrere tutti i numeri compresi tra 1 e 100. Per questo, dovremo usare a per ciclo continuo.

#! / bin / bash per il numero in 1 ... 100; fai eco $ numero fatto

Ci sono molte cose nuove che vale la pena notare in questo esempio - che, a proposito, stampa tutti i numeri da 1 a 100, un numero alla volta.

  • Il per la sintassi in Bash è: per VARIABLE in RANGE; fare COMANDO fatto.
  • Le parentesi graffe si trasformeranno 1 ... 100 in un intervallo nel nostro esempio. Sono utilizzati anche in altri contesti, che esamineremo a breve.
  • fare e per sono in realtà due comandi separati. Se vuoi posizionare due comandi su una singola riga, dovrai separarli in qualche modo. Un modo è usare il punto e virgola. In alternativa è possibile scrivere il codice senza un punto e virgola spostandosi fare alla seguente riga.
#! / bin / bash per il numero in 1 ... 100 fai eco $ numero fatto

La prima decisione

Ora che sappiamo come stampare tutti i numeri compresi tra 1 e 100, è tempo di prendere la nostra prima decisione.

#! / bin / bash per il numero in 1 ... 100; fai se [$ ((numero% 3)) -eq 0]; quindi echo "Fizz" echo $ number fi done

Questo esempio mostrerà "Fizz" per i numeri divisibili per 3. Di nuovo, dobbiamo fare i conti con un po 'di nuova sintassi. Prendiamoli uno per uno.

  • se ... poi ... altro ... fi - questa è la sintassi classica per a Se dichiarazione in Bash. Certo, il altro parte è facoltativa, ma è necessaria per la nostra logica in questo caso.
  • se COMMAND-RETURN-VALUE; poi… - Se verrà eseguito se il valore di ritorno del comando è zero. Sì, la logica in Bash è a base zero, il che significa che i comandi che vengono eseguiti correttamente escono con un codice di 0. Se qualcosa va storto, d'altra parte, verrà restituito un intero positivo. Per semplificare le cose: si considera qualsiasi cosa diversa da 0 falso.
  • Le espressioni matematiche in Bash sono specificate da doppie parentesi. $ ((Numero% 3)) restituirà il valore residuo della divisione della variabile, numero, per 3. Si prega di notare che non abbiamo usato $ all'interno della parentesi - solo di fronte a loro.

Ci si potrebbe chiedere dove sia il comando nel nostro esempio. Non c'è solo una parentesi con una strana espressione? Bene, si scopre che [ è in realtà un comando eseguibile. Per giocare con questo, prova i seguenti comandi nella tua console.

csaba @ csaba ~ $ che [/ usr / bin / [csaba @ csaba ~ $ [0 -eq 1] csaba @ csaba ~ $ echo $? 1 csaba @ csaba ~ $ [0 -eq 0] csaba @ csaba ~ $ echo $? 0

Mancia: Il valore di uscita di un comando viene sempre restituito nella variabile, ? (punto interrogativo). Viene sovrascritto dopo l'esecuzione di ogni nuovo comando.


Verifica Buzz

Stiamo andando bene finora. Abbiamo "Fizz"; ora facciamo la parte "Buzz".

#! / bin / bash per il numero in 1 ... 100; fai se [$ ((numero% 3)) -eq 0]; quindi echo "Fizz" elif [$ ((numero% 5)) -eq 0]; quindi echo "Buzz" altrimenti echo $ numero finito

Sopra, abbiamo introdotto un'altra condizione di divisibilità per 5: il Elif dichiarazione. Questo, ovviamente, si traduce in altrimenti se, e verrà eseguito se il comando successivo lo restituisce vero (o 0). Come puoi osservare, le affermazioni condizionali all'interno [] vengono solitamente valutati con l'aiuto di parametri, come -eq, che sta per "uguale".

Per la sintassi, arg1 OP arg2, OPERAZIONE è uno di -eq, -NE, -lt, -Le, -gt, o -ge. Questi operatori aritmetici binari restituiscono vero Se arg1 è uguale a, non uguale a, minore di, minore o uguale a, maggiore di, o maggiore di o uguale a arg2, rispettivamente.arg1 e arg2 possono essere numeri interi positivi o negativi. - Manuale di Bash

Quando si tenta di confrontare le stringhe, è possibile utilizzare il ben noto == segno, o anche un singolo segno di uguale farà. != ritorna vero quando le stringhe sono diverse.


Ma il codice non è abbastanza corretto

Finora, il codice viene eseguito, ma la logica non è corretta. Quando il numero è divisibile sia per 3 che per 5, la nostra logica farà eco solo a "Fizz". Modifichiamo il nostro codice per soddisfare l'ultima esigenza di FizzBuzz.

#! / bin / bash per il numero in 1 ... 100; fai se [$ ((numero% 3)) -eq 0]; quindi output = "Fizz" fi se [$ ((numero% 5)) -eq 0]; quindi output = "$ output Buzz" fi se [-z $ output]; quindi echo $ numero else echo $ output; fatto

Ancora una volta, abbiamo dovuto apportare una manciata di cambiamenti. Il più notevole è l'introduzione di una variabile, e quindi la concatenazione di "Buzz" ad esso, se necessario. Le stringhe in bash sono in genere definite tra virgolette doppie ("). Anche le virgolette singole sono utilizzabili, ma per semplificare la concatenazione, i doppi sono la scelta migliore. qualche testo $ variabile un altro testo"sostituirà variabile $ con i suoi contenuti. Quando si desidera concatenare variabili con stringhe senza spazi tra di esse, è preferibile inserire il nome della variabile tra parentesi graffe. Nella maggior parte dei casi, come PHP, non sei obbligato a farlo, ma aiuta molto quando si tratta della leggibilità del codice.

Mancia: Non è possibile confrontare stringhe vuote. Ciò restituirebbe un parametro mancante.

Perché argomenti dentro [] sono trattati come parametri, per "[", devono essere diversi da una stringa vuota. Quindi questa espressione, anche se logica, produrrà un errore: [$ output! = ""]. Ecco perché l'abbiamo usato [-z $ output], che ritorna vero se la stringa ha una lunghezza pari a zero.


Metodo di estrazione per l'espressione logica

Un modo per migliorare il nostro esempio è estrarre in funzioni l'espressione matematica dal Se dichiarazioni, in questo modo:

#! / bin / bash function isDivisibleBy return $ (($ 1% $ 2) per il numero in 1 ... 100; fare se isDivisibleBy $ numero 3; quindi output = "Fizz" fi se isDivisibleBy $ numero 5; quindi output = "$ output Buzz" fi se [-z $ output]; quindi echo $ numero else echo $ output; fatto

Abbiamo preso le espressioni confrontando il resto con zero e le abbiamo spostate in una funzione. Ancor di più, abbiamo eliminato il confronto con zero, perché zero significa vero per noi. Dobbiamo solo restituire il valore dall'espressione matematica - molto semplice!

Mancia: La definizione di una funzione deve precedere la sua chiamata.

In Bash, puoi definire un metodo come function func_name commands; . Facoltativamente, esiste una seconda sintassi per la dichiarazione delle funzioni: func_name () comandi; . Quindi, possiamo rilasciare la stringa, funzione e aggiungi "()" dopo il suo nome. Personalmente preferisco questa opzione, come esemplificato nell'esempio sopra. È più esplicito e ricorda i linguaggi di programmazione tradizionali.

Non è necessario specificare i parametri per una funzione in Bash. L'invio di parametri a una funzione viene eseguito semplicemente enumerandoli su di essi dopo la chiamata di funzione separata da spazi bianchi. Non mettere virgole o parentesi nella chiamata di funzione - non funzionerà.

I parametri ricevuti vengono assegnati automaticamente alle variabili in base al numero. Il primo parametro va a $ 1, il secondo a $ 2, e così via. La variabile speciale, $ 0 fa riferimento al nome del file dello script corrente.

Giochiamo con i parametri

#! / bin / bash function exampleFunc echo $ 1 echo $ 0 IFS = "X" echo "$ @" echo "$ *" esempioFunc "uno" "due" "tre"

Questo codice produrrà il seguente risultato:

csaba @ csaba ~ $ ./parametersExamples.sh one ./parametersExamples.sh one two thre oneXtwoXthre

Analizziamo la fonte, riga per riga.

  • L'ultima riga è la chiamata di funzione. Lo chiamiamo con tre parametri di stringa.
  • La prima riga dopo lo shebang è la definizione della funzione.
  • La prima riga nella funzione emette il primo parametro: "uno". Fin qui molto semplice.
  • La seconda riga mostra il nome del file dello script corrente. Ancora una volta, molto semplice.
  • La terza riga cambia il separatore di caratteri predefinito nella lettera "X". Di default, questo è "" (uno spazio). Ecco come Bash sa come sono separati i parametri.
  • La quarta riga emette una variabile speciale, $ @. Rappresenta tutti i parametri come una singola parola, esattamente come specificato nella chiamata di funzione.
  • La linea finale emette un'altra variabile speciale, $ *. Rappresenta tutti i parametri, presi uno a uno e concatenati con la prima lettera della variabile IFS. Ecco perché il risultato è oneXtwoXthre.

Restituzione di stringhe da funzioni

Come ho notato in precedenza, le funzioni in Bash possono restituire solo interi. Come tale, scrivere ritorna "una stringa" sarebbe un codice non valido Tuttavia, in molte situazioni, è necessario più di uno zero o uno. Possiamo refactoring il nostro esempio di FizzBuzz in modo che, nel per dichiarazione, faremo solo una chiamata di funzione.

#! / bin / bash function isDivisibleBy return $ (($ 1% $ 2)) function fizzOrBuzz if isDivisibleBy $ 1 3; quindi output = "Fizz" fi se isDivisibleBy $ 1 5; quindi output = "$ output Buzz" fi se [-z $ output]; quindi echo $ 1 else echo $ output; fi per il numero in 1 ... 100; fai il numero $ fizzOrBuzz fatto

Bene, questo è il primo passo. Abbiamo appena estratto tutto il codice in una funzione, chiamato fizzOrBuzz, e quindi sostituito numero di $ con $ 1. Tuttavia, tutto l'output si verifica in fizzOrBuzz funzione. Vogliamo uscire dal per loop con un eco dichiarazione, in modo che possiamo anteporre ogni riga con un'altra stringa. Dobbiamo catturare il fizzOrBuzz uscita della funzione.

# [...] per il numero in 1 ... 100; do echo "-'fizzOrBuzz $ numero '" fizzBuzzer = $ (numero $ fizzOrBuzz) echo "- $ fizzBuzzer" fatto

Abbiamo aggiornato il nostro per loop solo un po '(nessun altro cambiamento). Ora abbiamo fatto eco a tutto due volte in due modi diversi per esemplificare le differenze tra le due soluzioni per lo stesso problema.

La prima soluzione per acquisire l'output di una funzione o un altro comando consiste nell'usare i backtick. Nel 99% dei casi, funzionerà perfettamente. Puoi semplicemente fare riferimento a una variabile nei backtick con il loro nome, come abbiamo fatto con numero di $. Le prime poche righe dell'output dovrebbero ora apparire come:

csaba @ csaba ~ / Personal / Programming / NetTuts / Nozioni di base di scripting / Sorgenti BASH $ ./fizzBuzz.sh -1 -1 -2 -2 -Fizz -Fizz -4 -4 -Buzz -Buzz -Fizz -Fizz -7 -7

Come puoi vedere, tutto è duplicato. Stesso risultato.

Per la seconda soluzione, abbiamo scelto di assegnare prima il valore restituito a una variabile. In quel compito, abbiamo usato $ (), che, in questo caso, forchetta lo script, esegue il codice e restituisce l'output.


;, && e ||

Ti ricordi che abbiamo usato il punto e virgola qua e là? Possono essere utilizzati per eseguire diversi comandi scritti sulla stessa riga. Se li separi con il punto e virgola, verranno semplicemente eseguiti.

Un caso più sofisticato è da usare && tra due comandi. Sì, questo è un AND logico; significa che il secondo comando verrà eseguito solo se ritorna il primo vero (esce con 0). Questo è utile; possiamo semplificare il Se dichiarazioni in questi stenografi:

#! / bin / bash function isDivisibleBy return $ (($ 1 $ $ 2)) function fizzOrBuzz isDivisibleBy $ 1 3 && output = "Fizz" isDivisibleBy $ 1 5 && output = "$ output Buzz" se [-z $ output ]; quindi echo $ 1 else echo $ output; fi per il numero in 1 ... 100; do echo "-'fizzOrBuzz $ numero '" fatto

Come nostra funzione, isDivisibleBy restituisce un valore di ritorno appropriato, possiamo quindi utilizzare && per impostare la variabile che vogliamo. Cosa c'è dopo && sarà eseguito solo se la condizione è vero. Allo stesso modo, possiamo usare || (carattere a doppia pipa) come OR logico. Ecco un rapido esempio qui sotto.

csaba @ csaba ~ $ echo "bubu" || echo "bibi" bubu csaba @ csaba ~ $ echo false || echo "bibi" false csaba @ csaba ~ $

Pensieri finali

Così lo fa per questo tutorial! Spero che tu abbia raccolto una manciata di nuovi suggerimenti e tecniche per scrivere i tuoi script di Bash. Grazie per la lettura e restate sintonizzati per articoli più avanzati su questo argomento.