Si has seguido esta serie recordarás que en la temporada 01 se repasó rápidamente por la sintaxis y comandos más utilizados en Bash desde la shell. En la nota anterior se dio una breve introducción al scripting con Bash por medio del bien conocido “Hola mundo”, ahora para continuar con ese aprendizaje será necesario hacer una breve pausa en la sintaxis propia del lenguaje, para descubrir de manera más profunda lo que implica hacer un script en Bash.
La mayoría de los programadores tendemos a escribir los scripts como escribiríamos un programa en cualquier otro lenguaje de programación, en el cual se puedan utilizar distintos paradigmas de programación como lo es el caso de Python o Perl. Sin embargo Bash es puramente procedural y está enfocado a la ejecución de comandos, la potencia de Bash es visible cuando se necesita escribir scripts cortos y simples, como ya se mencionó en la nota anterior, si se desea escribir un script más grande y complejo es necesario involucrar a Python o Perl. Por lo que Bash es más productivo si se piensa en su línea de comandos como un entorno de desarrollo en lugar de escribirlo directamente en un editor de texto.
Ejemplo
Supón que tienes la siguiente carpeta de imágenes en alguna carpeta de tu computadora, algunas de ellas son .png y otras .PNG y por alguna razón necesitas que todas tengan la extensión .PNG en mayúsculas. La carpeta se vería de la siguiente forma:
Para resolver ese problema puedes ejecutar el siguiente comando:
$ find . -type f -name '*.png' | > grep -v no-modificar | > while read nombre_archivo; > do echo mv $nombre_archivo ${nombre_archivo/.png/.PNG}; > done
Desmenuzando el comando
find . -type f -name '*.png'
este comando encuentra archivos de tipo ordinario que en su nombre contengan al final la cadena .png.
grep -v no-modificar
este comando busca todas las líneas que no contienen el patrón no-modificar.
while read nombre_archivo; do echo mv $nombre_archivo ${nombre_archivo/ .png/.PNG}; done
este bloque de comandos imprime en la shell el <nombre_arvhivo><sustitución de patrón> recuerda que Linux marca diferencia entre mayúsculas y minúsculas, por lo que en este caso sustituye el patrón .png por .PNG mientras siga leyendo archivos.
Los pipes |
tienen como función enviar la salida del comando [1] como entrada del comando [2] y, la salida del comando [2] como entrada del comando [3].
Acertijo: ¿Ya sabes cuál es su salida?
Como se puede observar en la imagen, el comando muestra todos los archivos que terminan con el patrón .png y que además no contienen el patrón no-modificar y, finalmente por cada archivo con esas características imprime en la shell su nombre original y su nombre sustituyendo el patrón .png por .PNG.
Ahora estamos seguros que nuestro script efectivamente da solución a nuestro problema y entonces sí podemos ejecutarlo. Agregamos el siguiente comando | bash -x
como ya se explicó en párrafos anteriores el pipe |
indica que va a recibir la salida del comando anterior y la usará como entrada de la nueva instrucción, bash crea una nueva instancia de Bash y ejecuta cada uno de los comandos que recibe, la opción -x
imprime en la shell cada comando que va a ser ejecutado, de esta manera se puede comprobar o monitorear que Bash en efecto está realizando lo esperado. Si no se pone el último comando entonces se tendría que revisar nuevamente la carpeta para observar que en efecto se llevaron a cabo las acciones esperadas, lo que llevaría un poco más de tiempo para realizar sus tareas a un administrador.
$ find . -type f -name '*.png' | > grep -v no-modificar | > while read nombre_archivo; > do echo mv $nombre_archivo ${nombre_archivo/.png/.PNG}; > done | > bash -x
Tal vez te preguntes ¿por qué no eliminar solamente el echo
en lugar de usar bash -x
? La respuesta es simple: se podrían cometer errores si se modifica el script que ya se probó, y entonces el resultado ya no sería el que se espera. En su lugar se usa bash -x
para evitar modificar el script de forma errónea, además de que no podríamos monitorear en tiempo real lo que está haciendo el script.
¿Y si queremos usar nuevamente ese comando? ¿se debe escribir nuevamente? Recordando la nota edición de comandos de la primera temporada, Bash utiliza los mismos comandos que Emacs, así que para recorrer hacia atrás la lista de comandos recientemente utilizados basta con oprimir <Control + P> y esto está muy bien si se desea hacer en el momento, pero si lo que se quiere es utilizarlo en un futuro no inmediato entonces el comando indicado es fc
.
fc
Este se usa para listar, editar o reejecutar comandos que han sido previamente utilizados, es decir que se encuentran en el historial.
$ fc [-e nombre_editor] [-lnr] [primero] [último]
Opciones del comando
Algunas opciones del comando son:
-e
nombre_editor selecciona el editor a usar, por defecto es FCEDIT, después EDITOR y finalmente vi.
-l
muestra la lista de comandos en lugar de mandarlos a edición.
-n
omite los números de los comandos a mostrar.
-r
muestra primero los comandos más recientes.
Regresando al ejemplo, si se ejecuta fc
en la terminal, abrirá el editor que tiene por defecto y mostrará en él la última línea de comandos ejecutada.
Entonces ahora sí se puede guardar como script, solo es necesario agregar la línea del shebang, un par de comentarios sobre su funcionamiento y finalmente guardarlo con la extensión .sh. Y ya está listo para ser utilizado en otra ocasión.
En esta nota aprendiste a desarrollar un script directamente en línea de comandos, enviar la salida a STDOUT para asegurarte que en efecto realiza lo que quieres y así ejecutar con toda confianza los comandos mientras verificas que realizan lo indicado. Finalmente aprendiste a utilizar el comando fc para guardar el nuevo script y poder utilizarlo después. Espero que entre más avances en esta serie sientas cada vez más curiosidad por continuar, en la siguiente nota revisaremos Bash: manual en línea ¡Hasta pronto!