Mostrando entradas con la etiqueta script. Mostrar todas las entradas
Mostrando entradas con la etiqueta script. Mostrar todas las entradas

martes, 29 de julio de 2014

¿Dónde se ubica este script?

Introducción


Al escribir un script a veces interesa saber en qué directorio está ubicado, por ejemplo, para acceder a algún recurso (un fichero de datos o de configuración asociado.)

Muchos scripts se alojan en un directorio propiedad del usuario y se ejecutan desde el propio directorio con la ruta "./". En tal caso, se podría acceder a un recurso con esa misma ruta. Sin embargo, nada impide ejecutarlo desde otro directorio, o incluso invocarlo desde otro script, con lo que esa forma de referenciar los recursos fallaría.

Aproximaciones


Algunas soluciones burdas, pero efectivas en muchos casos, son tan simples como:
  • Alojar el script y sus recursos en el mismo directorio.
  • Alojar los recursos en determinado directorio fijo y poner toda la ruta cada vez que se referencien desde el script (es mucho mejor poner una variable, claro).
Sin embargo, si deseamos reubicar el script o sus recursos, esas soluciones no sirven o implican al menos editar el script.

Solución


El comando readlink permite obtener el nombre canónico (con su ruta completa) de un fichero.
El comando which permite encontrar los ejecutables que coinciden con el argumento indicado. Por defecto, sólo muestra la ruta completa del primero que se encuentra recorriendo las rutas de la variable de entorno PATH.
En un script la variable $0 corresponde al nombre del propio script.
El comando dirname permite eliminar el sufijo no correspondiente al directorio de una ruta (normalmente el nombre de un fichero junto a su extensión). Es decir, extraer hasta el último directorio de una ruta.

Combinando todo lo anterior, es posible obtener el nombre real del fichero que se está ejecutando y a partir de él el directorio donde está ubicado:

MYSELF="$(which $(readlink -f ${0}))"
MY_PATH="$(dirname $(which $(readlink -f ${0})))"

La variable MYSELF contendrá el nombre canónico del script y la variable MY_PATH el directorio donde está alojado.

Ejemplo


Si quieres probar que funciona en varios casos, puedes hacer lo siguiente:

#!/bin/bash

MYSELF="$(which $(readlink -f ${0}))"
MY_PATH="$(dirname $(which $(readlink -f ${0})))"
echo "\$0=${0}"
echo "\$MYSELF=   ${MYSELF}"
echo "\$MY_PATH=  ${MY_PATH}"

  • Crear un script con las líneas que anteriores llamado "cat".
  • Poner el script en un directorio cualquiera de tu home.
  • Ir a /tmp/ y crear un enlace simbólico al script ejecutando
    $ ln -s /home/_your_user/.../cat
  • Alterar la variable PATH en la sesión actual:
    $ PATH=/tmp/:$PATH
  • Acceder a cualquier directorio y ejecutar
    $ cat something
En lugar de ejecutarse el comando cat del sistema, se ejecutará el script, mostrando su nombre canónico y el directorio donde está, aunque en realidad, se esté ejecutando el enlace simbólico que se puso en el directorio /tmp/.

Conclusión


Probablemente haya alguna solución mejor, más elegante o más precisa, pero hasta la fecha, a mi me ha bastado con esta.

Enlaces


Enlaces de interés relacionados con este artículo:

(Actualizado 29/07/2014)

domingo, 29 de junio de 2014

Cómo mejorar scripts en bash

Introducción


Si has escrito scripts en algún intérprete de comandos como bash probablemente hayas tenido errores porque al haber escrito mal el nombre de una variable, el intérprete la define en ese momento vacía.

También se te habrá dado el caso de querer interrumpir la ejecución desde que algún comando falle, ya que por defecto, la ejecución de un script no se interrumpe aunque falle algún comando. Por ejemplo, si cambias de directorio (cd) y luego borras ficheros (rm), si falla el comando cd el resultado del comando rm puede ser desastroso.

Parámetros de configuración


El bash se puede configurar para abortar la ejecución de un script si se utiliza una variable que no ha sido inicializada:

#Expresiones equivalentes
set -o nounset
set -u

Esa opción es muy útil y puede evitarte muchos accidentes y quebraderos de cabeza.

También se puede configurar que se interrumpa la ejecución del script si falla algún comando:

#Expresiones equivalentes
set -o errexit
set -e

OJO, se considera fallar que el comando devuelva un valor distinto de 0.

Hay más parámetros que te pueden interesar como "pipefail" por ejemplo.

Ejemplos


Con este pequeño script, activando o desactivando los parámetros anteriores, puedes comprobar tú mismo como cambia el comportamiento del bash:

set -o nounset
set -o errexit
###############################################################################

#echo "Using mode: nounset"
echo "This command is executed: OK."
echo "This command fails because this variable has not been initialized <${VAR_UNDEF}>."
echo "This command is not executed because previous command failed."

#echo "Using mode: errexit"
echo "This command is executed: OK."
#Next command fails because the file doesn't exist.
cat "/tmp/unexistent.file"
echo "This command is not executed because previous command failed."

Si quieres hacer pruebas on-line, puedes usar este enlace:


Observaciones


Respecto al modo "errexit" hay mucha controversia (más abajo hay algunos enlaces si te interesa el tema). Hay que tener muy en cuenta que se aborta cuando un comando devuelve algo distinto de 0, lo cual no tiene por qué significar necesariamente que haya fallado. De hecho, hay comandos que intencionadamente devuelven valores distintos de 0 sin tratarse de errores.

Otra cosa que se comenta por ahí, es que algunas de las características (como "pipefail") van cambiando de una versión de bash a otra.
 

Conclusión


En mi humilde opinión, el modo "nounset" debería ser el comportamiento por defecto, pero bueno, para gustos los colores.

Yo considero que el modo "errexit" es una herramienta muy útil que el programador debe saber cuando usar y cuando no.

A veces interesa buscar si el comportamiento de alguna herramienta es configurable, porque muchas veces, el comportamiento por defecto no nos conviene.

Enlaces


Enlaces de interés relacionados con este artículo:

(Actualizado 29/06/2014)