Scrivere uno script di shell da zero

Scrivere script di shell può essere piuttosto scoraggiante, soprattutto perché la shell non è la più amichevole delle lingue da usare. Tuttavia, spero di mostrarti in questo tutorial che lo scripting di shell non è così difficile o spaventoso come potresti aspettarti.

Per questo tutorial, scriveremo uno script che rende il processo di utilizzo del framework di test Jasmine un po 'più semplice. In realtà, non userei questo script oggi; Vorrei usare Grunt.js o qualcosa di simile. Comunque, ho scritto questo script prima che Grunt fosse in giro, e ho scoperto che scrivere era un ottimo modo per stare più a tuo agio con lo scripting di shell, ecco perché lo stiamo usando.

Una nota: questo tutorial è liberamente associato al mio prossimo corso Tuts + Premium, "Advanced Command Line Techniques". Per saperne di più su praticamente qualsiasi cosa in questo tutorial, rimanete sintonizzati per il rilascio di quel corso. Di seguito in questo tutorial, verrà indicato come "il corso".

Quindi, il nostro script, che io chiamo jazz, avrà quattro caratteristiche principali:

  • Scaricherà Jasmine dal Web, decomprimerà ed eliminerà il codice di esempio.
  • Creerà i file JavaScript e i relativi file spec associati e li pre-riempirà con un po 'di codice template.
  • Aprirà i test nel browser.
  • Mostrerà il testo di aiuto, che delinea quanto sopra.

Iniziamo con il file di script.


Passaggio 1: creazione del file

Scrivere uno script di shell è utile solo se puoi usarlo dal terminale; per poter usare i tuoi script personalizzati sul terminale, devi metterli in una cartella che si trova nel tuo terminale SENTIERO variabile (puoi vedere il tuo SENTIERO variabile eseguendo echo $ PATH). Ho creato un ~ / Bin cartella (dove ~ è la home directory) sul mio computer, ed è lì che mi piace mantenere script personalizzati (se fai lo stesso, dovrai aggiungerlo al tuo percorso). Quindi, basta creare un file, chiamato jazz, e mettilo nella tua cartella.

Ovviamente, dovremo rendere eseguibile quel file; altrimenti, non saremo in grado di eseguirlo. Possiamo farlo eseguendo il seguente comando:

 chmod + x jazz

Ora che possiamo effettivamente eseguire lo script, aggiungiamo una parte molto importante. Tutti gli script di shell dovrebbero iniziare con uno shebang). Come dice Wikipedia, questa dovrebbe essere la prima riga della sceneggiatura; indica quale interprete o shell, con cui questo script deve essere eseguito. Utilizzeremo semplicemente una shell standard di base:

 #! / Bin / sh

Va bene, con tutto ciò che è stato configurato, siamo pronti per iniziare a scrivere il codice vero e proprio.


Passo 2 - Delineare il flusso degli script

In precedenza, ho sottolineato quali dovrebbero essere le diverse caratteristiche del nostro script di shell. Ma come farà lo script a sapere quale funzione eseguire? Useremo una combinazione di un parametro shell e un'istruzione case. Quando eseguiremo lo script dalla riga di comando, useremo un sottocomando, come questo:

 jazz init jazz creare SomeFile jazz eseguire aiuto jazz

Questo dovrebbe sembrare familiare, specialmente se hai usato Git:

 git init git status git commit

Basato su quel primo parametro (dentro, creare, correre, Aiuto), la nostra dichiarazione del caso deciderà cosa eseguire. Tuttavia, abbiamo bisogno di un caso predefinito: cosa succede se non viene fornito un primo parametro o viene visualizzato un primo parametro non riconosciuto? In questi casi, mostreremo il testo della guida. Quindi iniziamo!


Passaggio 3: scrittura del testo della guida

Iniziamo con il Se affermazione che controlla il nostro primo parametro:

 se [$ 1] poi # fai altro # mostra help fi

All'inizio potresti essere un po 'confuso, perché una shell Se la dichiarazione è piuttosto diversa da un linguaggio di programmazione "normale" Se dichiarazione. Per avere una migliore comprensione di esso, guarda lo screencast sulle dichiarazioni condizionali nel corso. Questo codice controlla la presenza di un primo parametro ($ 1); se è lì, eseguiremo il poi codice; altro, mostreremo il testo della guida.

È una buona idea avvolgere la stampa del testo di aiuto in una funzione, perché dobbiamo chiamarla più di una volta. Abbiamo bisogno di definire la funzione prima che venga chiamata, quindi la metteremo in cima. Mi piace, perché ora, non appena apro il file, vedo la documentazione per lo script, che può essere un utile promemoria quando si torna al codice che non si vede da un po '. Senza ulteriori indugi, ecco il Aiuto funzione:

 function help () echo "jazz - Un semplice script che rende l'uso del framework di test Jasmine in un progetto standalone un po 'più semplice." echo "echo" jazz init - include jasmine nel progetto "; echo" jazz create FunctionName - crea ./src/FunctionName.js ./spec/FunctionNameSpec.js "; echo" jazz run - esegue test nel browser ";

Ora, sostituiscilo # mostra aiuto funzione con una chiamata al Aiuto funzione.

 altro aiuto fi

Passaggio 4: scrivere la dichiarazione del caso

Se c'è un primo parametro, dobbiamo capire quale sia. Per questo, usiamo a Astuccio dichiarazione:

 caso "$ 1" in init) ;; creare) ;; correre) ;; *) Aiuto ;; esac

Passiamo il primo parametro al Astuccio economico; quindi, dovrebbe corrispondere a una delle quattro cose: "init", "create", "run", o il nostro jolly, caso predefinito. Si noti che non abbiamo un caso esplicito di "aiuto": questo è solo il nostro caso predefinito. Funziona, perché qualsiasi cosa diversa da "init", "create" e "run" non sono comandi che riconosciamo, quindi dovrebbe ottenere il testo di aiuto.

Ora siamo pronti per scrivere il codice funzionale e inizieremo con jazz init.


Step 5 - Preparare il gelsomino con jazz init

Tutto il codice che scriviamo qui andrà nei nostri dentro) caso, dalla dichiarazione del caso di cui sopra. Il primo passo è quello di scaricare la versione standalone di Jasmine, che arriva in un file zip:

 echo "Downloading Jasmine ..." arricciatura -sO $ JASMINE_LINK

Per prima cosa facciamo eco a un piccolo messaggio e quindi usiamo arricciare per scaricare lo zip. Il S flag lo rende silenzioso (nessun output) e il O flag salva il contenuto dello zip in un file (altrimenti lo reindirizzerebbe allo standard). Ma che cosa è con quello $ JASMINE_LINK variabile? Bene, puoi inserire il link effettivo al file zip lì, ma preferisco inserirlo in una variabile per due motivi: in primo luogo, ci impedisce di ripetere parte del percorso, come vedrai tra un minuto. Secondo, con quella variabile nella parte superiore del file, rende facile cambiare la versione di Jasmine che stiamo usando: basta cambiare quella variabile. Ecco la dichiarazione delle variabili (l'ho messa al di fuori di Se affermazione, vicino alla cima):

 JASMIME_LINK = "http://cloud.github.com/downloads/pivotal/jasmine/jasmine-standalone-1.3.1.zip"

Ricorda, non ci sono spazi attorno agli uguali: accedi a quella linea.

Ora che abbiamo il nostro file zip, possiamo decomprimerlo e preparare il contenuto:

 decomprimere -q basename $ JASMINE_LINK rm -rf basename $ JASMINE_LINK src / *. js spec / *. js

In due di queste linee, stiamo usando basename $ JASMINE_LINK; il basename comando semplicemente riduce un percorso al nome base: così path / to / file.zip diventa giusto file.zip. Questo ci permette di usarlo $ JASMINE_LINK variabile per fare riferimento al nostro file zip locale.

Dopo aver decompresso, cancelleremo quel file zip, così come tutti i file JavaScript nel src e spec le directory. Questi sono i file di esempio di Jasmine e non ne abbiamo bisogno.

Successivamente, abbiamo un problema solo per Mac da affrontare. Per impostazione predefinita, quando si scarica qualcosa da Internet su un Mac, quando si tenta di eseguirlo per la prima volta, verrà richiesto di confermare che si desidera eseguirlo. Questo a causa dell'attributo esteso com.apple.quarantine che Apple inserisce il file. Dobbiamo rimuovere questo attributo.

 se quale xattr> / dev / null && ["xattr SpecRunner.html"=" com.apple.quarantine "] then xattr -d com.apple.quarantine SpecRunner.html fi

Iniziamo controllando la presenza di xattr comando, perché non esiste su alcuni sistemi Unix (non sono sicuro, ma potrebbe essere un programma solo per Mac). Se hai guardato lo screencast del corso sui condizionali, saprai che possiamo passare qualsiasi comando a Se; se ha uno stato di uscita di qualsiasi altra cosa 0, è falso. Se quale trova il xattr comando, uscirà con 0; altrimenti, uscirà con 1. In ogni caso, quale mostrerà un po 'di output; possiamo impedire che ciò venga mostrato reindirizzandolo a / Dev / null (questo è un file speciale che scarta tutti i dati scritti su di esso).

Quella doppia e commerciale è un AND booleano; è lì per la seconda condizione che vogliamo controllare. Cioè, fa il SpecRunner.html avere questo attributo? Possiamo semplicemente eseguire il xattr comando sul file e confrontarlo con la stringa che ci aspettiamo. (Non possiamo solo aspettarci che il file abbia l'attributo, perché puoi effettivamente disattivare questa funzione in Mac OS X, e riceverai un errore quando proverai a rimuoverlo se il file non ha l'attributo).

Quindi se xattr viene trovato e il file ha l'attributo, lo rimuoveremo con il file d (per eliminare) bandiera. Piuttosto semplice, giusto?

Il passo finale è modificare SpecRunner.html. Attualmente contiene tag script per i file JavaScript di esempio che abbiamo eliminato; dovremmo anche cancellare quei tag script. Mi è capitato di sapere che questi tag di script coprono le righe da 12 a 18 nei file. Quindi, possiamo usare l'editor di stream sed per cancellare quelle linee:

 sed -i "" '12, 18d 'SpecRunner.html echo "Jasmine inizializzato!"

Il io la bandiera dice sed per modificare il file in posizione o per salvare l'output dal comando allo stesso file passato; la stringa vuota dopo la bandiera significa che non vogliamo sed per eseguire il backup del file per noi; se lo volessi, potresti semplicemente inserire un'estensione di file in quella stringa (come .bak, ottenere SpecRunner.html.bak).

Infine, faremo sapere all'utente che Jasmine è stato inizializzato. E questo è tutto per il nostro jazz init comando.


Passaggio 6: creazione di file con il jazz crea

Quindi, consentiremo ai nostri utenti di creare file JavaScript e i relativi file spec associati. Questa parte del codice andrà nella sezione "crea" di Astuccio dichiarazione che abbiamo scritto in precedenza.

 se [$ 2] poi # crea file else echo "per favore includi un nome per il file" fi

Quando si usa il jazz crea, dobbiamo includere un nome per il file come secondo parametro: jazz crea View, per esempio. Lo useremo per creare src / View.js e spec / ViewSpec.js. Quindi, se non c'è un secondo parametro, ricorderemo all'utente di aggiungerne uno.

Se c'è un nome di file, inizieremo creando questi due file (all'interno di poi parte sopra):

 echo "function $ 2 () \ n \ n"> src / $ 2.js echo "descrivi ('$ 2', function () \ n \ n);" > spec / $ 2Spec.js

Certo, puoi mettere tutto ciò che vuoi nel tuo src file. Sto facendo qualcosa di essenziale qui; così jazz crea View creerà src / View.js con questo:

 function View () 

Potresti sostituirlo per primo eco linea con questo:

 echo "var $ 2 = (function () \ n \ tvar $ 2Prototype = \ n \ n \ t; \ n \ n \ treturn \ n \ t \ tcreate: function (attrs) \ n \ t \ t \ tvar o = Object.create ($ 2Prototype); \ n \ t \ t \ textend (o, attrs); \ n \ t \ t \ treturn o; \ n \ t \ t \ n \ t; \ n ());" > src / $ 2.js

E poi jazz crea View risulterà in questo:

 var View = (function () var ViewPrototype = ; return create: function (attrs) var o = Object.create (ViewPrototype); extend (o, attrs); return o;; ()) ;

Quindi, davvero, la tua immaginazione è il limite. Ovviamente, vorrai che il file spec sia il codice standard standard di Jasmine, che è quello che ho sopra; ma puoi modificare ciò che vuoi comunque.

Il prossimo passo è aggiungere i tag di script per questi file SpecRunner.html. All'inizio, questo potrebbe sembrare complicato: come possiamo aggiungere linee al centro di un file a livello di programmazione? Ancora una volta, lo è sed questo fa il lavoro.

 sed -i "" "11a \\ \\  "SpecRunner.html

Iniziamo come prima: la modifica sul posto senza backup. Quindi il nostro comando: alla riga 11, vogliamo aggiungere le due righe seguenti. È importante sfuggire alle due nuove linee, in modo che vengano visualizzate nel testo. Come puoi vedere, questo semplicemente inserisce quei due tag script, esattamente ciò di cui abbiamo bisogno per questo passaggio.

Possiamo finire con qualche risultato:

 echo "Creato:" echo "\ t- src / $ 2.js" echo "\ t- spec / $ 2Spec.js" echo "Modificato:" echo "\ t- SpecRunner.html"

E questo è il jazz crea!


Passaggio 7: esecuzione delle specifiche con corsa jazz

L'ultimo passaggio consiste nell'eseguire effettivamente i test. Questo significa aprire il SpecRunner.html file in un browser. C'è un po 'di avvertimento qui. Su Mac OS X, possiamo usare il Aperto comando per aprire un file nel suo programma predefinito; questo non funzionerà su nessun altro sistema operativo, ma è così che lo faccio qui. Sfortunatamente, non c'è un vero modo multipiattaforma per farlo, che io sappia. Se stai usando questo script sotto Cygwin su Windows, puoi usare cygstart al posto di Aperto; altrimenti, prova a cercare su google "[il tuo sistema operativo] script di script per aprire il browser" e guarda cosa ti viene in mente. Sfortunatamente, alcune versioni di Linux (almeno Ubuntu, nella mia esperienza) hanno un Aperto comando che è per qualcosa di completamente diverso. Tutto questo per dire, il tuo chilometraggio con il seguente può variare.

if ["'which open'" = '/ usr / bin / open'] quindi apri SpecRunner.html else echo "Per favore apri SpecRunner.html nel tuo browser" fi

Ormai sai esattamente cosa fa questo: se ce l'abbiamo Aperto, apriremo SpecRunner.html, altrimenti, stamperemo semplicemente un messaggio che dice all'utente di aprire il file nel browser.

In origine, quello Se condizione sembrava così:

se quale apri> / dev / null

Come abbiamo fatto con xattr, ha appena verificato l'esistenza di Aperto; tuttavia, da quando ho scoperto che c'è un altro Aperto comando su Linux (anche sul mio server Ubuntu, che non può nemmeno aprire un browser!), ho pensato che sarebbe stato meglio confrontare il percorso del Aperto programma, poiché quello di Linux è a / Bin / aperto (ancora, almeno sul server Ubuntu).

Tutte queste parole in più Aperto potrebbe sembrare una scusa per la mia mancanza di una buona soluzione, in realtà indica qualcosa di importante sulla linea di comando. Non confondere la comprensione del terminale con la comprensione della configurazione di un computer. Questo tutorial e il corso associato ti hanno insegnato un po 'di più sulla shell Bash (e sulla shell Z), ma ciò non significa che ogni computer che utilizzerai sarà configurato allo stesso modo; ci sono molti modi per installare nuovi comandi (o diverse versioni di comandi), così come rimuovere comandi. Sviluppatore di caveat.

Bene, questa è l'intera sceneggiatura! Eccolo di nuovo, tutti insieme:

 #! / bin / sh function help () echo "jazz - Un semplice script che rende l'uso del framework di test Jasmine in un progetto standalone un po 'più semplice." echo "" echo "jazz init - include jasmine nel progetto"; echo "jazz create FunctionName - crea ./src/FunctionName.js ./spec/FunctionNameSpec.js"; echo "jazz run - esegue test nel browser";  JASMIME_LINK = "http://cloud.github.com/downloads/pivotal/jasmine/jasmine-standalone-1.3.1.zip" se [$ 1], quindi il caso "$ 1" in init) echo "Downloading Jasmine ..." curl - sO $ JASMIME_LINK unzip -q 'basename $ JASMIME_LINK' rm 'basename $ JASMIME_LINK' src / *. js spec / *. js se quale xattr> / dev / null && ["'xattr SpecRunner.html'" = "com.apple .quarantine "] then xattr -d com.apple.quarantine SpecRunner.html fi -i-" "" 12,18d "SpecRunner.html echo" Jasmine inizializzato! " ;; create) if [$ 2] then echo "function $ 2 () \ n \ n"> ./src/$2.js echo "Descrivi ('$ 2', function () \ nit ('runs'); \ n );" > ./spec/$2Spec.js sed -i "" "11a \\ \\  "SpecRunner.html echo" Creato: "echo" \ t- src / $ 2.js "echo" \ t- spec / $ 2Spec.js "echo" Modificato: "echo" \ t- SpecRunner.html "else echo" per favore aggiungi un nome per il file 'fi ;; "run") se ["' ​​che apre '" =' / usr / bin / open '] quindi apri ./SpecRunner.html else echo "Per favore apri SpecRunner.html nel tuo browser "fi ;; *) help; ;; esac else help; fi

Bene, dai, provaci!

 progetto mkdir progetto cd jazz init jazz create Dog # edit src / Dog.js e spec / DogSpec.js jazz run

A proposito, se vuoi divertirti un po 'di più con questo progetto, puoi trovarlo su Github.


Conclusione

Così il gioco è fatto! Abbiamo appena scritto uno script di shell di livello intermedio; non era poi così male, vero? Non dimenticare di rimanere sintonizzato per il mio prossimo corso Tuts + Premium; imparerai molto di più su molte delle tecniche utilizzate in questo articolo, così come innumerevoli altri. Buon divertimento sul terminale!