Escribir un script de shell desde cero

Escribir scripts de shell puede ser bastante desalentador, principalmente porque el shell no es el más fácil de usar. Sin embargo, espero mostrarle en este tutorial que los scripts de shell no son tan difíciles ni tan aterradores como podría esperarse..

Para este tutorial, escribiremos un script que simplifique un poco el proceso de uso del marco de prueba Jasmine. En realidad, no usaría este script hoy; Yo usaría Grunt.js o algo similar. Sin embargo, escribí este script antes de que existiera Grunt, y descubrí que escribirlo resultó ser una excelente manera de sentirse más cómodo con los scripts de shell, por eso lo estamos utilizando.

Una nota: este tutorial está ligeramente relacionado con mi próximo curso Tuts + Premium, "Técnicas avanzadas de línea de comando". Para obtener más información acerca de casi cualquier cosa en este tutorial, estad atentos para el lanzamiento de ese curso. En lo sucesivo, en este tutorial, se lo denominará "el curso".

Por lo tanto, nuestro guión, que yo llamo jazz, Tendrá cuatro características principales:

  • Descargará Jasmine de la web, lo descomprimirá y eliminará el código de muestra..
  • Creará archivos JavaScript y sus archivos de especificaciones asociados, y los rellenará con un poco de código de plantilla.
  • Se abrirán las pruebas en el navegador..
  • Mostrará el texto de ayuda, que resume lo anterior..

Comencemos con el archivo de script.


Paso 1 - Creando el archivo

Escribir un script de shell solo es útil si puede usarlo desde el terminal; Para poder usar sus scripts personalizados en el terminal, debe colocarlos en una carpeta que se encuentre en el terminal de su terminal. CAMINO variable (puedes ver tu CAMINO variable ejecutando echo $ PATH). He creado un ~ / bin carpeta (donde ~ es el directorio de inicio en mi computadora, y ahí es donde me gusta guardar scripts personalizados (si haces lo mismo, tendrás que agregarlo a tu ruta). Entonces, simplemente crea un archivo, llamado jazz, y ponlo en tu carpeta.

Por supuesto, también tendremos que hacer ese archivo ejecutable; De lo contrario, no podremos ejecutarlo. Podemos hacer esto ejecutando el siguiente comando:

 chmod + x jazz

Ahora que podemos ejecutar el script, agreguemos una parte muy importante. Todos los scripts de shell deben comenzar con un shebang). Como dice Wikipedia, esta debe ser la primera línea del guión; indica con qué intérprete, o shell, debe ejecutarse este script. Solo vamos a utilizar un shell básico, estándar:

 #! / bin / sh

De acuerdo, con todo lo establecido, estamos listos para comenzar a escribir el código real.


Paso 2 - Esbozando el flujo de script

Anteriormente, señalé cuáles deberían ser las diferentes características de nuestro script de shell. Pero, ¿cómo sabrá el script qué función ejecutar? Usaremos una combinación de un parámetro de shell y una declaración de caso. Cuando ejecutamos el script desde la línea de comando, usaremos un subcomando, como este:

 jazz init jazz crear SomeFile jazz run jazz ayuda

Esto debería parecerte familiar, especialmente si has usado Git:

 git init git status git commit

Basado en ese primer parámetro (en eso, crear, correr, ayuda), nuestra declaración de caso decidirá qué ejecutar. Sin embargo, necesitamos un caso predeterminado: ¿qué sucede si no se da un primer parámetro, o si obtenemos un primer parámetro no reconocido? En esos casos, mostraremos el texto de ayuda. Entonces empecemos!


Paso 3 - Escribir el texto de ayuda

Comenzamos con el Si declaración que comprueba nuestro primer parámetro:

 si [$ 1] entonces # hacer otras cosas # mostrar ayuda fi

Puede que estés un poco confundido al principio, porque una concha Si declaración es bastante diferente de un lenguaje de programación "regular" Si declaración. Para comprenderlo mejor, vea el resumen de pantalla sobre las declaraciones condicionales en el curso. Este código comprueba la presencia de un primer parámetro ($ 1); si está ahí, ejecutaremos el entonces código; más, mostraremos el texto de ayuda.

Es una buena idea envolver la impresión del texto de ayuda en una función, porque necesitamos llamarlo más de una vez. Necesitamos definir la función antes de que sea llamada, así que la pondremos en la parte superior. Me gusta esto, porque ahora, tan pronto como abro el archivo, veo la documentación para el script, lo cual puede ser un recordatorio útil al volver al código que no ha visto en mucho tiempo. Sin más preámbulos, aquí está el ayuda función:

 function help () echo "jazz: un script simple que simplifica un poco más el uso del marco de prueba de Jasmine en un proyecto independiente". echo "echo" jazz init - incluye jazmín en el proyecto "; echo" jazz crea FunctionName - crea ./src/FunctionName.js ./spec/FunctionNameSpec.js "; echo" jazz run - ejecuta pruebas en el navegador ";

Ahora, simplemente reemplaza eso # mostrar ayuda funciona con una llamada al ayuda función.

 otra cosa ayuda fi

Paso 4 - Escribiendo la declaración del caso

Si hay un primer parámetro, tenemos que averiguar cuál es. Para ello, utilizamos un caso declaración:

 caso "$ 1" en init) ;; crear) ;; correr) ;; *) ayuda ;; esac

Pasamos el primer parámetro al caso declaración; luego, debe coincidir con una de cuatro cosas: "init", "create", "run", o nuestro comodín, caso predeterminado. Tenga en cuenta que no tenemos un caso explícito de "ayuda": es solo nuestro caso predeterminado. Esto funciona porque cualquier cosa que no sea "init", "crear" y "ejecutar" no son comandos que reconocemos, por lo que debería obtener el texto de ayuda.

Ahora estamos listos para escribir el código funcional, y comenzaremos con inicio de jazz.


Paso 5 - Preparar jazmín con inicio de jazz

Todo el código que escribamos aquí irá dentro de nuestro en eso) caso, de la declaración del caso anterior. El primer paso es descargar la versión independiente de Jasmine, que viene en un archivo zip:

 echo "Descargando Jasmine ..." curl -sO $ JASMINE_LINK

Primero hacemos eco de un pequeño mensaje, y luego usamos rizo para descargar el zip. los s la bandera la silencia (sin salida) y la O flag guarda el contenido del archivo zip en un archivo (de lo contrario, lo canalizaría a la salida estándar). Pero que hay con eso $ JASMINE_LINK ¿variable? Bueno, podría poner el enlace real al archivo zip allí, pero prefiero ponerlo en una variable por dos razones: primero, nos impide repetir parte de la ruta, como verá en un minuto. En segundo lugar, con esa variable cerca de la parte superior del archivo, facilita el cambio de la versión de Jasmine que estamos usando: solo cambia esa variable. Aquí está esa declaración de variable (la puse fuera de la Si declaración, cerca de la parte superior):

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

Recuerda, no hay espacios alrededor del signo igual en esa línea.

Ahora que tenemos nuestro archivo zip, podemos descomprimirlo y preparar los contenidos:

 descomprimir -q nombre base $ JASMINE_LINK rm -rf nombre base $ JASMINE_LINK src / *. js spec / *. js

En dos de estas líneas, estamos usando nombre base $ JASMINE_LINK; la nombre base comando solo reduce una ruta al nombre base: asi ruta / a / archivo.zip se convierte en justo archivo.zip. Esto nos permite usar ese $ JASMINE_LINK variable para hacer referencia a nuestro archivo zip local.

Después de descomprimir, eliminaremos ese archivo zip, así como todos los archivos JavaScript en el src y especulación directorios Estos son los archivos de muestra con los que viene Jasmine, y no los necesitamos.

A continuación, tenemos un problema solo para Mac con el que lidiar. De forma predeterminada, cuando descarga algo de Internet en una Mac, cuando intenta ejecutarlo por primera vez, se le solicitará que confirme que desea ejecutarlo. Esto es debido al atributo extendido com.apple.quarantine que Apple pone en el archivo. Necesitamos eliminar este atributo..

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

Comenzamos comprobando la presencia de la xattr comando, porque no existe en algunos sistemas Unix (no estoy seguro, pero puede ser un programa solo para Mac). Si viste el curso de screencast sobre condicionales, sabrás que podemos pasar cualquier comando a Si; Si tiene un estado de salida de cualquier cosa menos 0, es falso. Si cual encuentra el xattr comando, saldrá con 0; De lo contrario, saldrá con 1. En cualquier caso, cual mostrará alguna salida; Podemos evitar que eso se muestre redirigiéndolo a / dev / null (este es un archivo especial que descarta todos los datos escritos en él).

Ese doble ampersand es un booleano AND; Está ahí para la segunda condición que queremos comprobar. Es decir, hace el SpecRunner.html tiene ese atributo? Simplemente podemos ejecutar el xattr comando en el archivo y compare su salida con la cadena que estamos esperando. (No podemos esperar que el archivo tenga el atributo, ya que en realidad puede desactivar esta función en Mac OS X, y obtendremos un error al intentar eliminarlo si el archivo no tiene el atributo).

Así que si xattr se encuentra y el archivo tiene el atributo, lo eliminaremos, con el re (para eliminar) bandera. Bastante sencillo, a la derecha?

El paso final es editar. SpecRunner.html. Actualmente, contiene etiquetas de scripts para los archivos de muestra de JavaScript que eliminamos; También deberíamos eliminar esas etiquetas de scripts. Resulta que las etiquetas de script abarcan las líneas 12 a 18 en los archivos. Entonces, podemos usar el editor de secuencias. sed para borrar esas lineas:

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

los yo bandera dice sed para editar el archivo en su lugar, o para guardar la salida del comando en el mismo archivo que pasamos; la cadena vacía después de la bandera significa que no queremos sed para hacer una copia de seguridad del archivo para nosotros; si quisiera eso, podría poner una extensión de archivo en esa cadena (como .bak, Llegar SpecRunner.html.bak).

Finalmente, le haremos saber al usuario que Jasmine ha sido inicializado. Y eso es todo para nuestro inicio de jazz mando.


Paso 6 - Creando archivos con crear jazz

A continuación, vamos a permitir que nuestros usuarios creen archivos JavaScript y sus archivos de especificaciones asociados. Esta parte del código irá a la sección "crear" de la caso declaración que escribimos anteriormente.

 si [$ 2] entonces # crear archivos de lo contrario echo ", por favor incluya un nombre para el archivo" fi

Cuando usas crear jazz, Necesitamos incluir un nombre para el archivo como segundo parámetro: jazz crear vista, por ejemplo. Usaremos esto para crear src / View.js y spec / ViewSpec.js. Entonces, si no hay un segundo parámetro, le recordamos al usuario que agregue uno.

Si hay un nombre de archivo, comenzaremos creando esos dos archivos (dentro de la entonces parte arriba):

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

Por supuesto, puedes poner lo que quieras en tu src expediente. Estoy haciendo algo básico aquí; asi que jazz crear vista creará src / View.js con este:

 función Vista () 

Podrías reemplazar eso primero eco en línea con esto:

 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; \norte());" > src / $ 2.js

Y entonces jazz crear vista resultará en esto:

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

Entonces, realmente, tu imaginación es el límite. Por supuesto, querrás que el archivo de especificaciones sea el código de especificación estándar de Jasmine, que es el que tengo arriba; Pero puedes modificar eso como quieras..

El siguiente paso es agregar las etiquetas de script para estos archivos a SpecRunner.html. Al principio, esto puede parecer complicado: ¿cómo podemos agregar líneas a la mitad de un archivo mediante programación? Una vez más, es sed eso hace el trabajo.

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

Comenzamos como lo hicimos antes: edición in situ sin copia de seguridad. Entonces nuestro comando: en la línea 11, queremos añadir las dos líneas siguientes. Es importante escapar de las dos nuevas líneas, para que aparezcan en el texto. Como puede ver, esto solo inserta esas dos etiquetas de scripts, exactamente lo que necesitamos para este paso.

Podemos terminar con alguna salida:

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

Y eso es crear jazz!


Paso 7 - Ejecutando las especificaciones con carrera de jazz

El último paso es ejecutar las pruebas. Esto significa abrir la SpecRunner.html archivo en un navegador. Hay una pequeña advertencia aquí. En Mac OS X, podemos utilizar el abierto comando para abrir un archivo en su programa predeterminado; Esto no funcionará en ningún otro sistema operativo, pero así es como lo hago aquí. Desafortunadamente, no hay una verdadera forma multiplataforma para hacer esto, que yo sepa. Si está utilizando este script en Cygwin en Windows, puede usar cygstart en lugar de abierto; de lo contrario, intente buscar en Google "[su sistema operativo] shell script abrir el navegador" y ver qué se le ocurre. Desafortunadamente, algunas versiones de Linux (al menos Ubuntu, en mi experiencia) tienen un abierto El comando es para algo completamente diferente. Todo esto para decir, su kilometraje con lo siguiente puede variar.

si ["'que está abierto'" = '/ usr / bin / open'] entonces abra SpecRunner.html else echo "Por favor, abra SpecRunner.html en su navegador" fi

A estas alturas, ya sabes exactamente lo que hace esto: si tenemos abierto, abriremos SpecRunner.html, de lo contrario, simplemente imprimiremos un mensaje para que el usuario abra el archivo en el navegador..

Originalmente que Si condición se veía así:

si se abre> / dev / null

Como hicimos con xattr, acaba de comprobar la existencia de abierto; Sin embargo, desde que me enteré de que hay una diferente abierto comando en Linux (incluso en mi servidor Ubuntu, que ni siquiera puede abrir un navegador), pensé que sería mejor comparar la ruta del abierto programa, ya que el de Linux está en / bin / abierto (de nuevo, al menos en el servidor Ubuntu).

Toda esta extravagancia acerca de abierto Puede sonar como una excusa para mi falta de una buena solución, en realidad señala algo importante acerca de la línea de comando. No confunda el hecho de entender el terminal con entender la configuración de una computadora. Este tutorial, y el curso asociado, le han enseñado un poco más sobre el shell Bash (y el shell Z), pero eso no significa que cada computadora que use se configurará de la misma manera; Hay muchas formas de instalar nuevos comandos (o diferentes versiones de comandos), así como eliminar comandos.. Desarrollador de la advertencia.

Bueno, eso es todo el guión! Aquí está de nuevo, todos juntos:

 #! Ayuda de la función / bin / sh () echo "jazz: un script simple que simplifica un poco el uso del marco de prueba de Jasmine en un proyecto independiente". echo "" echo "jazz init - incluye jazmín en el proyecto"; echo "jazz create FunctionName - crea ./src/FunctionName.js ./spec/FunctionNameSpec.js"; echo "jazz run - ejecuta pruebas en el navegador";  JASMIME_LINK = "http://cloud.github.com/downloads/pivotal/jasmine/jasmine-standalone-1.3.1.zip" si [$ 1] entonces caso "$ 1" en init) echo "Descargando Jasmine ..." curl - sO $ JASMIME_LINK descomprimir -q 'nombre base $ JASMIME_LINK' rm 'nombre base $ JASMIME_LINK' src / *. js spec / *. js if que xattr> / dev / null && ["'xattr SpecRunner.html" .quarantine "] luego xattr -d com.apple.quarantine SpecRunner.html fi sed -i" "" 12,18d "SpecRunner.html echo" Jasmine initialized! " ;; crear) si [$ 2] entonces echo "function $ 2 () \ n \ n"> ./src/$2.js echo "describe ('$ 2', function () \ nit ('run'); \ n ); " > ./spec/$2Spec.js sed -i "" "11a \\ \\  "SpecRunner.html echo" Creado: "echo" \ t- src / $ 2.js "echo" \ t- spec / $ 2Spec.js "echo" Editado: "echo" \ t- SpecRunner.html "else echo 'please agregue un nombre para el archivo 'fi ;; "ejecutar") si ["' que abre '" =' / usr / bin / open '] luego abra ./SpecRunner.html else echo "Por favor, abra SpecRunner.html en su navegador "fi ;; *) help;;;; es else help; fi

Bueno, vamos, dale una oportunidad!

 mkdir proyecto cd proyecto jazz init jazz crear Dog # edit src / Dog.js y spec / DogSpec.js jazz run

Por cierto, si quieres divertirte más con este proyecto, puedes encontrarlo en Github.


Conclusión

Así que ahí lo tienes! Acabamos de escribir un script de shell de nivel intermedio; Eso no fue tan malo, ¿verdad? No te olvides de estar atento a mi próximo curso Tuts + Premium; aprenderá mucho más acerca de muchas de las técnicas utilizadas en este artículo, así como de muchas otras. Diviértete en la terminal!