Écrire un script shell à partir de zéro

L'écriture de scripts shell peut être assez ardue, principalement parce que le shell n'est pas le langage le plus convivial à utiliser. Cependant, j'espère vous montrer dans ce tutoriel que les scripts shell ne sont en réalité pas aussi difficiles ni aussi effrayants qu'on pourrait le croire.

Pour ce tutoriel, nous allons écrire un script qui facilite un peu le processus d’utilisation du framework de test Jasmine. En fait, je n'utiliserais pas ce script aujourd'hui; Je voudrais utiliser Grunt.js ou quelque chose de similaire. Cependant, j’ai écrit ce script avant l’arrivée de Grunt et j’ai trouvé qu’il s’avérait un excellent moyen d’être plus à l’aise avec les scripts shell, c’est pourquoi nous l’utilisons..

Remarque: ce tutoriel est vaguement associé à mon prochain cours Tuts + Premium, "Techniques de ligne de commande avancées". Pour en savoir plus sur à peu près tout dans ce tutoriel, restez à l'écoute pour la publication de ce cours. Dans la suite de ce didacticiel, il sera appelé "le cours".

Donc, notre script, que j'appelle le jazz, aura quatre caractéristiques principales:

  • Il téléchargera Jasmine sur le Web, le décompressera et supprimera le code exemple..
  • Il créera des fichiers JavaScript et leurs fichiers de spécifications associés et les pré-remplira avec un peu de code de modèle..
  • Il ouvrira les tests dans le navigateur.
  • Il affichera le texte d'aide, qui décrit ce qui précède.

Commençons par le fichier de script.


Étape 1 - Création du fichier

L'écriture d'un script shell n'est utile que si vous pouvez l'utiliser depuis le terminal; pour pouvoir utiliser vos scripts personnalisés sur le terminal, vous devez les placer dans un dossier situé dans le terminal. CHEMIN variable (vous pouvez voir votre CHEMIN variable en exécutant echo $ PATH). J'ai créé un ~ / bin dossier (où ~ est le répertoire personnel) sur mon ordinateur, et c’est là que j’aime garder les scripts personnalisés (si vous faites la même chose, vous devrez l’ajouter à votre chemin). Donc, il suffit de créer un fichier, appelé le jazz, et le mettre dans votre dossier.

Bien sûr, nous devrons également rendre ce fichier exécutable; sinon, nous ne pourrons pas l'exécuter. Nous pouvons le faire en lançant la commande suivante:

 chmod + x jazz

Maintenant que nous pouvons réellement exécuter le script, ajoutons une partie très importante. Tous les scripts shell doivent commencer par un shebang). Comme le dit Wikipedia, cela devrait être la première ligne du script; il indique avec quel interpréteur ou quel shell ce script doit être exécuté. Nous allons juste utiliser un shell standard basique:

 #! / bin / sh

Très bien, avec tout ce qui est configuré, nous sommes prêts à commencer à écrire le code actuel..


Étape 2 - Présentation du déroulement du script

Un peu plus tôt, je vous ai montré quelles devraient être les différentes fonctionnalités de notre script shell. Mais comment le script saura-t-il quelle fonctionnalité exécuter? Nous allons utiliser une combinaison d'un paramètre shell et d'une instruction case. Lors de l'exécution du script à partir de la ligne de commande, nous utiliserons une sous-commande, comme celle-ci:

 jazz init jazz créer SomeFile jazz run aide jazz

Cela devrait vous paraître familier, surtout si vous avez utilisé Git:

 git init git status git commit

Basé sur ce premier paramètre (init, créer, courir, Aidez-moi), notre déclaration de cas décidera de ce qu’il faut exécuter. Cependant, nous avons besoin d'un cas par défaut: que se passe-t-il si aucun premier paramètre n'est donné ou si nous obtenons un premier paramètre non reconnu? Dans ces cas, nous afficherons le texte d'aide. Alors, commençons!


Étape 3 - Écrire le texte d'aide

Nous commençons par le si déclaration qui vérifie notre premier paramètre:

 si [$ 1] alors # faire d'autres choses # montrer l'aide fi

Vous pourriez être un peu confus au début, car un shell si déclaration est assez différente de celle d'un langage de programmation "normal" si déclaration. Pour mieux le comprendre, regardez le screencast sur les instructions conditionnelles du cours. Ce code vérifie la présence d’un premier paramètre (1 $) si c'est là, nous exécuterons le puis code; autre, nous allons montrer le texte d'aide.

C'est une bonne idée de placer l'impression du texte d'aide dans une fonction, car nous devons l'appeler plusieurs fois. Nous avons besoin de définir la fonction avant de l'appeler, nous allons donc la mettre en haut. Cela me plaît, car dès que j’ouvre le fichier, je vois la documentation du script, ce qui peut être un rappel utile lors du retour à un code que vous n’avez pas vu depuis longtemps. Sans plus tarder, voici la Aidez-moi une fonction:

 function help () echo "jazz - Un script simple qui simplifie un peu plus l'utilisation du framework de test Jasmine dans un projet autonome." echo "echo" jazz init - inclut le jasmin dans le projet "; echo" jazz create NomFonction - crée ./src/NomFonction.js ./spec/FunctionNameSpec.js "; echo" jazz exécute les tests dans le navigateur ";

Maintenant, remplacez ça # montrer de l'aide fonctionner avec un appel à la Aidez-moi une fonction.

 d'autre aide fi

Étape 4 - Rédaction de la déclaration de cas

S'il existe un premier paramètre, nous devons déterminer lequel il s'agit. Pour cela, nous utilisons un Cas déclaration:

 case "$ 1" dans init) ;; créer) ;; courir) ;; *) Aidez-moi ;; esac

Nous passons le premier paramètre à la Cas déclaration; ensuite, il devrait correspondre à l'une des quatre choses suivantes: "init", "create", "run", ou notre caractère générique, le cas par défaut. Notez que nous n'avons pas de cas explicite d '"aide": ce n'est que notre cas par défaut. Cela fonctionne, car rien d'autre que "init", "create" et "run" ne sont des commandes que nous reconnaissons, il devrait donc obtenir le texte d'aide.

Maintenant, nous sommes prêts à écrire le code fonctionnel, et nous allons commencer par init jazz.


Étape 5 - Préparer du jasmin avec init jazz

Tout le code que nous écrivons ici ira dans notre init) cas, de la déclaration de cas ci-dessus. La première étape consiste à télécharger la version autonome de Jasmine dans un fichier zip:

 echo "Téléchargement de Jasmine…" curl -sO $ JASMINE_LINK

Nous faisons d'abord écho à un petit message, puis nous utilisons boucle télécharger le zip. le s flag le rend silencieux (pas de sortie) et le O flag enregistre le contenu du zip dans un fichier (sinon, il le dirigerait vers la sortie standard). Mais c'est quoi ça $ JASMINE_LINK variable? Eh bien, vous pouvez mettre le lien réel vers le fichier zip ici, mais je préfère le mettre dans une variable pour deux raisons: premièrement, cela nous empêche de répéter une partie du chemin, comme vous le verrez dans une minute. Deuxièmement, avec cette variable près du haut du fichier, il est facile de changer la version de Jasmine que nous utilisons: changez simplement cette variable. Voici cette déclaration de variable (je la mets en dehors de la si déclaration, vers le haut):

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

Rappelez-vous, pas d'espaces autour du signe égal dans cette ligne.

Maintenant que nous avons notre fichier zip, nous pouvons le décompresser et en préparer le contenu:

 décompressez -q nom de base $ JASMINE_LINK rm -rf nom de base $ JASMINE_LINK src / *. js spec / *. js

Dans deux de ces lignes, nous utilisons nom de base $ JASMINE_LINK; la nom de base commande réduit simplement un chemin d'accès au nom de base: chemin / vers / fichier.zip devient juste fichier.zip. Cela nous permet d'utiliser cela $ JASMINE_LINK variable pour référencer notre fichier zip local.

Une fois le fichier décompressé, nous supprimerons ce fichier zip, ainsi que tous les fichiers JavaScript de la src et spec des répertoires. Voici les exemples de fichiers fournis par Jasmine, et nous n'en avons pas besoin.

Ensuite, nous devons traiter un problème concernant uniquement Mac. Par défaut, lorsque vous téléchargez quelque chose à partir d'Internet sur un Mac, lorsque vous essayez de l'exécuter pour la première fois, il vous est demandé de confirmer que vous souhaitez l'exécuter. Ceci est dû à l'attribut étendu com.apple.quarantine que Apple met sur le fichier. Nous devons supprimer cet attribut.

 si quel xattr> / dev / null && ["xattr SpecRunner.html"=" com.apple.quarantine "] puis xattr -d com.apple.quarantine SpecRunner.html fi

Nous commençons par vérifier la présence du xattr commande, car il n’existe pas sur certains systèmes Unix (je ne suis pas sûr, mais il s’agit peut-être d’un programme exclusivement Mac). Si vous avez regardé le screencast du cours sur les conditions, vous saurez que nous pouvons transmettre toute commande à si; si elle a un statut de sortie autre que 0, c'est faux. Si lequel trouve le xattr commande, il va sortir avec 0; sinon, il sortira avec 1. Dans tous les cas, lequel affichera une sortie; nous pouvons empêcher cela de montrer en le redirigeant vers / dev / null (Ceci est un fichier spécial qui supprime toutes les données qui y sont écrites).

Ce double esperluette est un booléen ET; c'est là pour la deuxième condition que nous voulons vérifier. Est-ce que le SpecRunner.html avoir cet attribut? Nous pouvons simplement exécuter le xattr commande sur le fichier et comparez sa sortie à la chaîne attendue. (Nous ne pouvons pas simplement nous attendre à ce que le fichier ait cet attribut, car vous pouvez réellement désactiver cette fonctionnalité sous Mac OS X et nous obtiendrons une erreur en essayant de la supprimer si le fichier ne possède pas cet attribut.).

Donc si xattr est trouvé et le fichier a l'attribut, nous allons l'enlever, avec le (pour supprimer) drapeau. Assez simple, à droite?

La dernière étape consiste à éditer SpecRunner.html. Actuellement, il contient des balises de scripts pour les exemples de fichiers JavaScript que nous avons supprimés. nous devrions également supprimer ces balises de scripts. Je sais que ces balises de script couvrent les lignes 12 à 18 dans les fichiers. Nous pouvons donc utiliser l'éditeur de flux sed supprimer ces lignes:

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

le je le drapeau raconte sed pour éditer le fichier à la place ou pour enregistrer le résultat de la commande dans le même fichier que celui que nous avons transmis; la chaîne vide après le drapeau signifie que nous ne voulons pas sed sauvegarder le fichier pour nous; si vous le vouliez, vous pourriez simplement mettre une extension de fichier dans cette chaîne (comme .bak, obtenir SpecRunner.html.bak).

Enfin, nous informerons l'utilisateur que Jasmine a été initialisé. Et c'est tout pour notre init jazz commander.


Étape 6 - Création de fichiers avec jazz créer

Ensuite, nous allons permettre à nos utilisateurs de créer des fichiers JavaScript et leurs fichiers de spécifications associés. Cette partie du code ira dans la section "Créer" du Cas déclaration que nous avons écrite plus tôt.

 si [$ 2] alors # créer des fichiers sinon echo "veuillez inclure un nom pour le fichier" fi

Lors de l'utilisation jazz créer, nous devons inclure un nom pour le fichier en tant que deuxième paramètre: jazz create Voir, par exemple. Nous allons utiliser cela pour créer src / View.js et spec / ViewSpec.js. Donc, s'il n'y a pas de second paramètre, nous rappelons à l'utilisateur d'en ajouter un.

S'il y a un nom de fichier, nous allons commencer par créer ces deux fichiers (à l'intérieur du puis partie ci-dessus):

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

Bien sûr, vous pouvez mettre ce que vous voulez dans votre src fichier. Je fais quelque chose de fondamental ici; alors jazz create Voir créera src / View.js avec ça:

 function Voir () 

Vous pouvez remplacer ce premier écho aligner avec ceci:

 echo "var $ 2 = (function () \ n \ tvar $ 2Prototype = \ n \ n \ t; \ n \ n \ treturn \ n \ t \ t \ tcreate: fonction (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

Et alors jazz create Voir entraînera ceci:

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

Alors, vraiment, votre imagination est la limite. Bien sûr, vous voudrez que le fichier de spécification soit le code de spécification standard de Jasmine, ce que j'ai ci-dessus; mais vous pouvez modifier cela comme bon vous semble.

La prochaine étape consiste à ajouter les balises de script pour ces fichiers à SpecRunner.html. Au début, cela peut sembler délicat: comment pouvons-nous ajouter des lignes au milieu d'un fichier par programme? Encore une fois, c'est sed ça fait le travail.

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

Nous commençons comme avant: éditer sur place sans sauvegarde. Puis notre commande: à la ligne 11, nous voulons ajouter les deux lignes suivantes. Il est important d'échapper aux deux nouvelles lignes afin qu'elles apparaissent dans le texte. Comme vous pouvez le constater, cela insère simplement ces deux balises de script, exactement ce dont nous avons besoin pour cette étape..

Nous pouvons terminer avec quelques sorties:

 echo "Créé:" echo "\ t-src / $ 2.js" echo "\ t-spec / $ 2Spec.js" echo "Edité:" echo "\ t-SpecRunner.html"

Et c'est jazz créer!


Étape 7 - Exécution des spécifications avec course de jazz

La dernière étape consiste à exécuter les tests. Cela signifie ouvrir le SpecRunner.html déposer dans un navigateur. Il y a un peu de mise en garde ici. Sur Mac OS X, nous pouvons utiliser le ouvrir commande pour ouvrir un fichier dans son programme par défaut; cela ne fonctionnera sur aucun autre système d'exploitation, mais c'est comme cela que je le fais ici. Malheureusement, il n'y a pas vraiment de moyen multiplateforme de le faire, à ma connaissance. Si vous utilisez ce script sous Cygwin sous Windows, vous pouvez utiliser cygstart au lieu de ouvrir; sinon, essayez de googler "[votre navigateur] shell script open browser" et voyez ce que vous proposez. Malheureusement, certaines versions de Linux (du moins Ubuntu, selon mon expérience) ont une ouvrir commande qui est pour quelque chose de complètement différent. Tout cela pour dire, votre kilométrage avec ce qui suit peut varier.

if ["'which open'" = '/ usr / bin / open'], puis ouvrez SpecRunner.html else echo "Veuillez ouvrir SpecRunner.html dans votre navigateur" fi

A présent, vous savez exactement ce que cela fait: si nous avons ouvrir, nous ouvrirons SpecRunner.html, sinon, nous allons simplement imprimer un message invitant l'utilisateur à ouvrir le fichier dans le navigateur..

À l'origine, si la condition ressemblait à ceci:

si ouvert> / dev / null

Comme nous l'avons fait avec xattr, il vient de vérifier l'existence de ouvrir; cependant, depuis que j'ai découvert qu'il y a un autre ouvrir commande sur Linux (même sur mon serveur Ubuntu, qui ne peut même pas ouvrir de navigateur!), j’ai pensé qu’il serait peut-être préférable de comparer le chemin du ouvrir programme, puisque celui de Linux est à / bin / open (encore une fois, au moins sur le serveur Ubuntu).

Tous ces mots supplémentaires à propos de ouvrir Cela peut sembler être une excuse pour mon manque de bonne solution, cela indique en fait quelque chose d’important à propos de la ligne de commande. Ne vous méprenez pas sur la compréhension du terminal avec la configuration de l’ordinateur. Ce tutoriel et le cours associé vous ont appris un peu plus sur le shell Bash (et le shell Z), mais cela ne signifie pas que chaque ordinateur que vous utilisez sera configuré de la même manière; il existe de nombreuses façons d'installer de nouvelles commandes (ou différentes versions de commandes), ainsi que de supprimer des commandes. Développeur avertissement.

Eh bien, c'est le script entier! Le voici à nouveau, tous ensemble:

 #! / bin / sh function help () echo "jazz - Un script simple qui simplifie un peu plus l'utilisation du framework de test Jasmine dans un projet autonome." echo "" echo "jazz init - inclure du jasmin dans le projet"; echo "jazz create FunctionName - crée ./src/FunctionName.js ./spec/FunctionNameSpec.js"; echo "jazz run - exécute des tests dans un navigateur";  JASMIME_LINK = "http://cloud.github.com/downloads/pivotal/jasmine/jasmine-standalone-1.3.1.zip" if [$ 1], la casse "$ 1" dans init) echo "Téléchargement de Jasmine…" curl - sO $ JASMIME_LINK unzip -q 'nom_base $ JASMIME_LINK' rm 'nom_base $ JASMIME_LINK' src / *. js spec / *. js si quelle xattr> / dev / null && ["'xattr SpecRunner.html" .quarantine "] puis xattr -d com.apple.quarantine SpecRunner.html fi-di" "" 12,18d "SpecRunner.html echo" Jasmine initialized! " ;; créer) si [$ 2] alors echo "function $ 2 () \ n \ n"> ./src/$2.js echo "decrire ('$ 2', function () \ nit ('s'exécute'); \ n ); " > ./spec/$2Spec.js sed -i "" "11a \\ \\  "SpecRunner.html echo" Créé: "echo" \ t-src / $ 2.js "echo" \ t-spec / $ 2Spec.js "echo" édité: "echo" \ t- SpecRunner.html "else echo" s'il vous plaît ajoutez un nom pour le fichier 'fi ;; "run") if ["" qui ouvre "" = "/" / usr / bin / open "] puis ouvrez ./SpecRunner.html else echo" Veuillez ouvrir SpecRunner.html dans votre navigateur "fi ;; *) help; ;; esac else help; fi

Eh bien, allez, essayez!

 mkdir projet cd projet jazz init jazz créer Dog # edit src / Dog.js et spec / DogSpec.js jazz run

À propos, si vous voulez avoir plus de plaisir avec ce projet, vous pouvez le trouver sur Github.


Conclusion

Donc là vous l'avez! Nous venons d'écrire un script shell de niveau intermédiaire; ce n'était pas si mal, maintenant, n'est-ce pas? N'oubliez pas de rester à l'écoute de mon prochain cours Tuts + Premium; vous en apprendrez beaucoup plus sur de nombreuses techniques utilisées dans cet article, ainsi que sur d'innombrables autres. Amusez-vous sur le terminal!