¿Para qué sirve BASH_SOURCE?

En el capítulo “Incluir otros ficheros en Bash” hablé sobre el problema de las rutas al incluir ficheros con source (o ‘.’) pero he recibido algunas consultas sobre qué es lo que hace exactamente BASH_SOURCE.

Si leemos lo que dice el manual de Bash (en inglés) no queda muy claro:

An array variable whose members are the source filenames where the corresponding shell function names in the FUNCNAME array variable are defined. The shell function ${FUNCNAME[$i]} is defined in the file ${BASH_SOURCE[$i]} and called from ${BASH_SOURCE[$i+1]}

Así que vamos a hacer nuestras propias investigaciones.

Empecemos creando un fichero llamado script.sh:

#!/bin/bash
echo ${BASH_SOURCE[@]}

y haciéndolo ejecutable:

$ chmod +x
$ ./script.sh
./script.sh

Bien, parece que nos muestra la ruta relativa del script en ejecución.

¿Qué es una ruta relativa?

Cuando ejecutamos un script lo hacemos desde la carpeta donde nos encontramos (carpeta actual o “working directory”). Para saber cuál es la carpeta actual podemos usar el comando pwd (Print Working Directory). Por ejemplo:

$ pwd
/home/gorka

Imagina que el fichero script.sh lo tengo en /home/gorka/pruebas/script.sh. Para ejecutarlo debería hacer:

$ pruebas/script.sh
pruebas/script.sh

En este caso $BASH_SOURCE nos da la ruta relativa a la carpeta de trabajo. Si la ruta completa es /home/gorka/pruebas/script.sh y ejecuto el script desde /home/gorka:

  • La carpeta de trabajo será /home/gorka.
  • La ruta relativa será pruebas/script.sh.

Si tienes alguna duda puedes añadir el comando pwd al fichero script.sh:

#!/bin/bash
pwd
echo ${BASH_SOURCE[@]}

y el resultado será:

/home/gorka
pruebas/script.sh

Incluyendo un segundo script

Vamos a modificar el fichero script.sh para que llame a otro fichero:

#!/bin/bash
pwd
echo ${BASH_SOURCE[@]}
. script1.sh

En este caso llamamos a script1.sh que tendrá el siguiente contenido:

#!/bin/bash
echo "Comienzo de script1.sh...."
echo ${BASH_SOURCE[@]}

Ahora volvamos a la carpeta /home/gorka/pruebas y ejecutemos desde ahí script.sh:

$ ./script
/home/gorka/pruebas
./script.sh
Comienzo de script1.sh....
script1.sh ./script.sh

Esto empieza a ser interesante, si te fijas en la última línea tenemos:

script1.sh ./script.sh

que es lo que nos muestra ${BASH_SOURCE[@]}.

NOTA: Puedes echar un vistazo al artículo sobre cómo funcionan los arrays en Bash.

Ahí podemos ver que $BASH_SOURCE es un array que contiene todos los ficheros que hemos ido incluyendo en orden inverso:

  • Elemento 0: el fichero actual. En este caso script1.sh.
  • Elemento 1: el fichero inicial que llamamos. En este caso script.sh.

Para que quede más claro podemos añadir un fichero más editando script1.sh:

#!/bin/bash
echo "Comienzo de script1.sh...."
echo ${BASH_SOURCE[@]}
. script2.sh

Y creando script2.sh:

#!/bin/bash
echo "Comienzo del script2.sh..."
echo ${BASH_SOURCE[@]}
echo ${BASH_SOURCE[0]}
echo ${BASH_SOURCE[1]}
echo ${BASH_SOURCE[2]}

Al ejecutar script.sh el resutlado será:

/home/gorka/pruebas
./script.sh
Comienzo de script1.sh....
script1.sh ./script.sh
Comienzo del script2.sh...
script2.sh script1.sh ./script.sh
script2.sh
script1.sh
./script.sh

Aquí podemos ver que:

${BASH_SOURCE[0]} → script2.sh
${BASH_SOURCE[1]} → script1.sh
${BASH_SOURCE[2]} → script.sh

Llamando al script con la ruta completa

Vamos a probar algo diferente, vamos a ejecutar el script usando la ruta completa:

$ /home/gorka/pruebas/script.sh
/home/gorka/pruebas
/home/gorka/pruebas/script.sh
Comienzo de script1.sh....
script1.sh /home/gorka/pruebas/script.sh
Comienzo del script2.sh...
script2.sh script1.sh /home/gorka/pruebas/script.sh
script2.sh
script1.sh
/home/gorka/pruebas/script.sh

En este caso el último elemento de $BASH_SOURCE contiene la ruta completa desde la que hemos ejecutado el script.

Resumen

Cada uno de los elementos del array $BASH_SOURCE es un fichero que se ha incluido desde otro script.

El último elemento es el script inicial que se ha ejecutado. A éste elemento se le añade la ruta que se ha utilizado para llamarlo (que puede ser relativa o una ruta completa).

Cada uno de los elementos del array (excepto el primero) ha sido incluido desde el elemento siguiente. Es decir script2.sh ha sido incluido por script1.sh y script1.sh ha sido incluido por script.sh.

Autor:
Nivel: Principiante
Palabras clave:
Fecha publicado:
Fecha actualizado: 13-01-2017

Otros capítulos de la misma serie

Este capítulo es parte de la serie: Introducción a Bash.

Y muchos más en preparación.

Disponible en los planes: Laravel hero PHP a tope