Este es uno de los capítulos del tutorial Scripts en Bash. Encontrarás los enlaces a todos los de capítulos, al final de este artículo.
En este nuevo capítulo del tutorial intentaré traerte algunas de las mejores prácticas para scripts en Bash, porque, uno de los grandes inconvenientes que tiene nuestra principal fuente de información y documentación, Internet, es precisamente la desinformación. Hay tal cantidad de información a lo largo y ancho de toda la red de redes que es relativamente fácil encontrar información errónea. No solo errónea porque su autor se haya equivocado al mostrarla, o porque simplemente no haya comprobado su veracidad.
Uno de los grandes problemas es la desactualización. Y esto de la desactualización, cuando hablamos de documentación técnica todavía tiene mas relevancia. Y tiene mas relevancia, en tanto en cuanto, cada cierto tiempo, se liberan nuevas versiones de un determinado software que deja obsoletas las versiones anteriores. Sin embargo, la documentación correspondiente a las versiones anteriores sigue estando ahí, lo cual lleva a confundirnos. Por esta razón, insistir en que este capítulo, intenta recopilar algunas de las mejores prácticas para scripts en Bash, con la intención de que seamos capaces de escribir cada día mejores scripts y con mas sentido, dejando de lado aquellas prácticas obsoletas.
Las mejores prácticas para tus scripts en Bash
Empezar tus scripts con un shebang
Recordarte antes que nada, que eso del shebang
no es ni mas ni menos que esa línea del script del tipo #!/bin/bash
, donde le informamos al sistema operativo cual es el intérprete de comandos que tiene que utilizar para trabajar con nuestro script.
Utilizar las opciones de Bash
Bash tiene una serie de opciones que permiten asegurar que nuestro código hace lo que tiene que hacer, o se comporta de una determinada manera. Así, por ejemplo, alguans de estas opciones son las siguientes,
set -o errexit
oset -e
hace que se salga de tu script inmediatamente cuando un comando falla.set -o nounset
oset -u
en el caso de que intentes utilizar una variable que no está declarada saldrá del script.set -o xtrace
oset -x
permite realizar una traza de lo que se ha ejecutado. En concreto yo lo utilizo para depurar mis scripts, y ver donde y porque se está produciendo un error.set -o pipefail
devuelve el estado del último comando que no fue cero.
Estas son algunas pocas de todas las opciones que tienes a tu disposición. Te recomiendo que le des un vistazo al manual de Bash, para tener información mucho mas detallada.
Convenciones con variables
Existen algunas convenciones en lo que al uso de variables se refiere. Algunas de estas convenciones las tienes que aplicar obligatoriamente, porque de otra forma arrojará un error, mientras que otras dependerá de ti, aunque lo aconsejable es que sigas estas buenas prácticas.
- Los nombres de las variables deben estar en mayúsculas. Este por ejemplo, es una recomendación, pero no es obligatorio. Así por ejemplo, un nombre de variable adecuado sería
ARCHIVO
. - Solo puedes utilizar, para los nombres de las variables, letras, números y el guión bajo. Ni espacios, ni guión medio, ni otros caracteres extraños. Además las variables no pueden comenzar con número. Es decir,
1ARCHIVO
es incorrecto, mientras queARCHIVO1
es correcto. - Entre la variable y el igual no puede haber espacios. Si intentas hacer
ARCHIVO = mi archivo
, te dará un error. Lo suyo esARCHIVO="mi archivo.txt"
- Utiliza siempre dobles comillas. Por ejemplo para el caso anterior, si quieres borrar el archivo, lo tienes que hacer con
rm "$ARCHIVO"
.
Anotaciones de variables
Bash permite varios tipos de anotaciones de variables, entre las que cabe destacar,
local
para variables locales dentro de una funciónreadonly
para definir una variable como de solo lectura
Lo recomendable es que utilices estas anotaciones en la medida de lo posible. De esta forma, tu código será mas seguro.
Desde luego para el uso de funciones, es una práctica mas que recomendable. Utilizando el readonly
, lo que pasa en la función se queda en la función.
Utiliza $()
Como sabes para ejecutar comandos en línea, puedes utilizar tanto la comilla inclinada, como $()
. Lo mas recomendable es que utilices este último, porque de esa forma el código será mucho mas legible. Pero no solo se trata de que sea mas legible, es que en ocasiones, es mucho mas sencillo utilizar esta solución, que no las comillas inclinadas, que casi resulta infernal hacerlo.
En mi caso, hace tiempo que he abandonado la comilla inclinada en favor de $()
, y como todo en esta vida, no es mas que acostumbrarte y ser práctico.
Los dobles corchetes
Esta práctica todavía no la tengo interiorizada, pero es algo, que me he puesto como meta para los próximos meses, con el objetivo de integrar mejores prácticas en Bash. Se trata de utilizar los dobles corchetes en favor de los simples para el caso de las comparaciones. Y es que el uso de los dobles corchetes es mucho mas consistente. Me remito al ejemplo de la página que cito en las notas. Si intentas esto
var=
[ "$var" < "a" ] && echo True
Esto te devolverá un error no existe el archivo o el directorio: a
. Esto es así porque Bash intenta abrir un archivo llamado a
. Para obtener el resultado esperado en el caso de utilizar corchetes simples
, tienes que recurrir a escapar <
.
Esta inconsistencia ya es problemática de por si, pero, imagina que este archivo exista y sobreescribas su contenido.
Sin embargo, si utilizas lo que te indico en la siguiente línea, el resultado es el esperado,
[[ "$var" < "a" ]] && echo True
Pero no solo está la ventaja de que utilizar dobles corchetes es mucho mas consistente que utilizar simple, sino que hay determinadas comparaciones que no puedes realizar utilizando corchete simple. Por ejemplo, suponiendo que tienes la variable v
definida de la siguiente forma v="abc123"
,
[[ "$v" == abc* ]]
. Cierto. Esta característica se conoce como gobbling, y es la misma que utilizas cuando estás en el terminal.[[ "$w" == "abc*" ]]
. Falso. En este caso estás haciendo una comparación literal, de ahí que el resultado sea Falso.[[ "$x" =~ [abc]+\d+ ]]
. Cierto. Los dobles corchetes te permite utilizar expresiones regulares, y ya solo por esto vale la pena adoptarlos. En alguna ocasión por utilizar para aprovechar las expresiones regulares, he mezclado, comparaciones con corchetes simples y corchetes dobles, y en lugar de conseguir un código mucho mas limpio, obtienes justo lo contrario de lo que buscas.
Evita utilizar archivos temporales
Yo soy el primero que en mas de una ocasión, para realizar determinadas operaciones, lo que he hecho es utilizar archivos temporales. Por ejemplo, si quieres comparar el contenido de dos páginas web, puedes descargarlas a tu equipo, y utilizando diff
, realizar una comparación. Sin embargo, en lugar de esto, también puedes utilizar el operador <()
, que te ayuda con estas tareas. Fíjate como se podría hacer de la misma forma,
diff <(wget -O - url1) <(wget -O - url2)
Depuración
Si en el caso de los dobles corchetes, te he comentado que estoy integrándolos ahora en mi rutina habitual, en el caso de la depuración, te digo, que hace tiempo que forma parte de flujo de trabajo. Y sin lugar a dudas, es una de las mejores prácticas en Bash, y en cualquier otro lenguaje de programación. Los scripts, no son nada oscuro, ni nada extraño, son simplemente unas pocas líneas de código. En este sentido, es realmente interesante poder seguir lo que sucede en tu script en tiempo real, y en su caso, descubrir porque no está funcionando, o porque simplemente no está haciendo lo que se supone que debería estar haciendo.
De esta forma, tienes distintas opciones para depurar tus scripts
bash -n script.sh
. Ejecuta el script en secobash -v script.sh
. Muestra cada comando que se ejecuta.bash -x script.sh
. Muestra cada comando expandido que se ejecuta.
Puedes combinar algunos de los anteriores según tus necesidades para obtener unos resultados u otros. Por ejemplo utilizando -vn
te mostrará cada uno de los comandos ejecutas pero ejecutando en seco, con lo que en caso de que uno falle, podrás verlo, pero sin el problema de que haga algún mal.
El vídeo
Más información,
Imagen de portada de Brett Jordan
¿Conocés ShellCheck (https://www.shellcheck.net/)?
Es muy útil para detectar código frágil en un script de bash, también es muy didáctico.
Se puede usar desde línea de comandos y hay extensiones para los editores populares como VS Code (https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck)
Hola Juan,
Si, lo tengo integrado directamente en Neovim.
Un saludo.
Gracias, buenas sugerencias.
Hay un comando shellcheck que se puede instalar en linux, o simplemente usar el servicio online en https://www.shellcheck.net/ – me ha dado muy buenos resultados también.