featured image

En 1996 instalé la primera versión de GNU/Linux en mi PC, compilé un código en C y no pude ejecutarlo. No sabía el porqué. El fichero estaba ahí y al igual que en DOS con los ficheros de tipo exe parecía poder ejecutarse. Tiempo después me dijeron que debía escribir la ruta relativa o absoluta al fichero o agregar la ruta actual, el punto (.) dentro del PATH, ¿pero por qué?

Al principio me frustraba mucho el hecho de crear un script o un programa y tener que ejecutarlo como ./miprograma en lugar de simplemente miprograma. Llegué a agregar dentro de mi .bashrc esta simple línea:

export PATH="$PATH:."

Para asegurarme de tener siempre la ruta local disponible y no tener que emplear una ruta relativa cuando el fichero a ejecutar se encontraba en la ruta actual. Pero incluso me llamó mucho más la atención cuando el script o el binario a ejecutar se encontraba en otro subdirectorio y veía a compañeros ejecutar:

$ ./target/miprograma

En lugar de tan solo escribir:

$ target/miprograma

Parecía que la combinación inicial ./ hacía pensar a muchas personas que si el binario se encontraba fuera de las rutas del PATH, entonces debía ser llamado siempre con esa combinación inicial.

Para profundizar un poco más en todo esto primero dejaremos claros algunos conceptos.

Programas, Binarios y Scripts

En todos los sistemas operativos nos encontramos con directorios y subdirectorios (o carpetas y subcarpetas), donde introducimos nuestros ficheros (o archivos). Estos archivos, dependiendo si tratamos con un sistema operativo Windows o tipo Unix, deben:

  • en Windows tener una extensión concreta como .exe principalmente, aunque también podemos hacer scripts con extensiones como .bat o .cmd. La extensión determina si se puede ejecutar o no.
  • en sistemas tipo Unix (GNU/Linux, macOS o FreeBSD entre otros) los ficheros deben tener un permiso de ejecución que permita al usuario ejecutar ese binario o script. La extensión no es importante.

Un fichero binario normalmente será un archivo generado por un compilador que permite ser ejecutado en el sistema operativo. Por tanto deberemos poder ejecutarlo sin problemas.

Un script será un archivo de texto plano que contenga instrucciones a ser interpretadas por un intérprete de comandos como puede ser bash, zsh, csh, powershell, cmd.exe o [fish][FISH] entre otros.

Rutas relativas y rutas absolutas

Ejecutando una consola o empleando Finder en macOS, o el Explorer en Windows, o cualquier otro visor de ficheros tendremos siempre una ruta donde nos encontramos. En sistemas Unix podemos emplear el comando pwd para obtener la ruta absoluta donde nos encontramos. En los gestores de ficheros normalmente aparecerá en la barra de direcciones la ruta absoluta donde nos encontramos.

Estas rutas mostradas están en dirección absoluta, según el sistema operativo la ruta puede ser

  • en Windows absoluta cuando comienza por la unidad seguida de una barra invertida (\), algo como C:\WIN\SYSTEM32 nos indica la ruta absoluta para el directorio SYSTEM32. La ruta relativa, en caso de encontrarnos en la unidad C: podría ser WIN\SYSTEM32 o si estamos en WIN puede ser simplemente SYSTEM32.
  • en sistemas tipo Unix absoluta es cuando comienza por una barra inclinada (/), algo como /usr/local/bin nos indica la ruta absoluta para el directorio bin. La ruta relativa, en caso de encontrarnos en / podría ser usr/local/bin o si estamos en /usr puede ser local/bin o si estamos en /usr/local puede ser simplemente bin.

Ejecutando programas

Entonces, hemos dicho que para ejecutar un programa especificando solo su nombre en sistemas tipo Unix deben cumplirse la premisa de estar localizado a través de alguna de las rutas disponibles en la variable de entorno PATH:

$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin

Esto quiere decir que si escribo en la shell el comando ls, este será buscado en las rutas:

  • /usr/local/bin
  • /usr/bin
  • /bin
  • /usr/sbin
  • /sbin
  • /opt/X11/bin

Y en ese orden. En este caso el fichero binario ls con permisos de ejecución se encuentra en la ruta /bin/ls, podemos verlo aquí:

$ whereis ls
/bin/ls

Por lo que buscó antes en dos sitios. La única forma de evitar la búsqueda es indicar una ruta relativa o absoluta. Eso quiere decir que si escribimos /sbin/ls obtendremos un error porque intenta ejecutar un fichero que no existe en la ruta proporcionada.

¿Qué hacemos por tanto si queremos ejecutar un script que acabamos de escribir en la ruta donde nos encontramos?

Pues tenemos dos opciones.

Directorio Binario Propio

Es normal si como usuario en sistemas tipo Unix generas muchos scripts, para poder llamarlos de forma rápida estés en la ruta que estés, te creas un directorio que casa normalmente con ~/bin o $HOME/bin. Siendo ~ un símbolo especial para indicar tu directorio personal como usuario.

Esto suele ser fácil porque los shells proporcionan ficheros de inicio, normalmente ~/.bashrc, o ~/.zshrc, o ~/.bash_profile, ... según la shell que emplees y el sistema operativo deberás buscar el fichero que corresponda. En ese fichero puedes modificar la variable PATH para cada vez que entres:

export PATH="$PATH:$HOME/bin"

En consolas como fish tan solo habría que crear un fichero:

$ echo 'set -gxa PATH ~/bin' >> ~/.config/fish/conf.d/mybin.fish

De esta forma al acceder a la consola la siguiente vez se habrá actualizado nuestro PATH y ahora ya sí podremos emplear cualquier script dentro de ese directorio sin necesidad de agregar siempre la ruta relativa.

Usar el infame ./

Sí. Otra forma es simular una ruta relativa hacia el propio directorio. Esto es posible gracias a los directorios especiales punto (.) y dos puntos (..). En sistemas Windows simplemente haciendo dir podrás verlos en cmd. En sistemas tipo Unix debemos ejecutar:

$ ls -a
.     ..     .bashrc     bin     prueba.sh

El directorio punto (.) es un enlace al directorio donde nos encontramos. Es recursivo. Si hacemos ls o ls . o ls ./. o ls ././. y así hasta el infinito siempre obtendremos el mismo resultado. No obstante el directorio (..) es un enlace al directorio anterior o padre. Esto nos permite realizar acciones como cd .. para llevarnos al directorio anterior.

Entonces, si necesitamos ejecutar el script prueba.sh del directorio actual, deberemos hacer:

$ ./prueba.sh

Si por contra hemos cambiado el directorio actual:

$ cd bin
$ ../prueba.sh

Esta vez la ruta relativa la construimos con el directorio de dos puntos (..) para referenciar al directorio padre donde se encuentra el script.

¿Por qué no agregamos el . en el PATH?

En principio, dicen que por problemas de seguridad. Porque si te cambias a un directorio de un usuario y ejecutas ls para ver su contenido y sucede que hay un binario ls malicioso, es dañino. Pero, la verdad es que esta teoría es fácil de desmontar porque si configuramos:

export PATH="$PATH:."

La búsqueda de la ruta actual la estamos anexando al final. Por lo tanto, debe encontrar siempre ls en su ruta original antes que en la ruta donde está el usuario en ese momento.

Realmente no se conoce el motivo que llevó a realizar este cambio en la versión 3 de Unix, donde rompieron con la herencia de Multics y en lugar de situar los scripts y programas dentro de /bin y posteriormente además /usr/bin cuando las limitaciones del momento les hicieron quedarse sin espacio, decidieron dejar esta búsqueda configurable por el usuario a través de la variable de entorno PATH.

Conclusiones

No tiene sentido actualmente no agregar el punto (.) AL FINAL de nuestra variable de entorno PATH según mi opinión. Al igual que no tiene sentido que cuando ejecutamos un binario o script en una ruta relativa antepongamos el prefijo ./ por creer que tiene un significado especial. Creo que son dejes que habrá que ir subsanando poco a poco.

¿Y tú? ¿Empleas el infame prefijo ./ para todo lo que ejecutas de forma local? ¿Sabías que podías eliminar su uso? ¿Qué otros usos extraños encuentras en Windows o sistemas Unix referentes a ejecución o uso de ficheros? ¡Déjanos tu comentario!