¿Cómo funciona la búsqueda en $ PATH debajo del capó?

8

Hay demasiados artículos / recursos en la web que enseñan a las personas cómo configurar la variable de entorno PATHpara que puedan usar la abreviatura de javao pythonetc. en lugar de la ruta absoluta en la interfaz de línea de comandos.

Lo que me interesa saber es qué hay detrás de escena cuando escribimos el comando y presionamos enter (similar a lo que sucede cuando escribes una URL en el navegador ).

Aquí está mi suposición:

  1. lea el comando (análisis / preproceso stdin para obtener los argumentos correctos $@)
  2. búsqueda de comando
  3. ejecución del comando (programa iniciado, consumo de memoria, stdout / stderr a shell)
  4. volver a representar el emulador por variables de entorno relevantes (p $PS#. ej . $PROMPT, etc.)

La parte que más quiero entender es la búsqueda de comandos. Obviamente, el $PATHes consumido por alguna función de fondo y separado por :/ ;como delimitadores, entonces, ¿qué pasó? ¿Usamos una tabla hash (clave: nombre base del archivo, valor: nombre de directorio absoluto del archivo) para almacenar los archivos binarios en esas RUTA u otros ganchos?

NOTA: Originalmente pensé que era una tabla hash, ya que puedo usar [ -z hash [command] ]para verificar si un comando está disponible en el entorno actual, pero cuando lo uso hash | grep pythonno obtengo nada de la salida mientras which pythonfunciona como se esperaba. (Creo que el mecanismo podría ser específico del shell, pero quiero obtener más información sobre él).

Xlee
fuente

Respuestas:

11

Como sospecha, el comportamiento exacto depende del shell, pero POSIX especifica un nivel básico de funcionalidad.

La búsqueda y ejecución de comandos para el lenguaje de comandos de shell estándar (que la mayoría de los shells implementan un superconjunto) tiene muchos casos, pero solo estamos interesados ​​por el momento en el caso en que PATHse usa. En ese caso:

se buscará el comando utilizando la variable de entorno PATH como se describe en Variables de entorno XBD

y

Si la búsqueda es exitosa:

[...]

el shell ejecuta la utilidad en un entorno de utilidad separado con acciones equivalentes a llamar a la execl()función [...] con el argumento de ruta establecido en el nombre de ruta resultante de la búsqueda.

En el caso fallido, la ejecución falla y se devuelve un código de salida de 127 con un mensaje de error.

Este comportamiento es consistente con la execvpfunción, en particular. Todas las exec*funciones aceptan el nombre de archivo de un programa para ejecutar, una secuencia de argumentos (que será la argvdel programa) y quizás un conjunto de variables de entorno. Para las versiones que utilizan la PATHbúsqueda, POSIX define que :

El archivo de argumento se utiliza para construir un nombre de ruta que identifica el nuevo archivo de imagen de proceso, el [...] prefijo de ruta para este archivo se obtiene mediante una búsqueda de los directorios pasados ​​como la variable de entorno RUTA


El comportamiento de PATH se define en otros lugares como:

Esta variable representará la secuencia de prefijos de ruta que ciertas funciones y utilidades aplican al buscar un archivo ejecutable conocido solo por un nombre de archivo. Los prefijos deben estar separados por un <colon> (':'). Cuando se aplica un prefijo de longitud distinta de cero a este nombre de archivo, se insertará una <barra inclinada> entre el prefijo y el nombre de archivo si el prefijo no termina en. Un prefijo de longitud cero es una característica heredada que indica el directorio de trabajo actual. Aparece como dos caracteres adyacentes ("::"), como un <colon> inicial que precede al resto de la lista, o como un <colon> al final del resto de la lista. Una aplicación estrictamente conforme utilizará un nombre de ruta real (como.) Para representar el directorio de trabajo actual en PATH.Se buscará en la lista de principio a fin, aplicando el nombre de archivo a cada prefijo, hasta que se encuentre un archivo ejecutable con el nombre especificado y los permisos de ejecución apropiados . Si el nombre de ruta que se busca contiene una <barra inclinada>, no se realizará la búsqueda a través de los prefijos de ruta. Si el nombre de ruta comienza con una <barra inclinada>, la ruta especificada se resuelve (consulte Resolución de nombre de ruta ). Si PATH no está establecido o está establecido en nulo, la búsqueda de ruta está definida por la implementación.

Eso es un poco denso, así que un resumen:

  1. Si el nombre del programa tiene un /(barra oblicua, U + 002F SOLIDUS), trátelo como una ruta de la manera habitual y omita el resto de este proceso. Para el shell, este caso técnicamente no surge (porque las reglas del shell ya lo habrán tratado).
  2. El valor de PATHse divide en partes en cada punto y luego cada componente se procesa de izquierda a derecha. Como un caso especial (histórico), un componente vacío de una variable no vacía se trata como .(el directorio actual).
  3. Para cada componente, el nombre del programa se agrega al final con una unión /y se verifica la existencia de un archivo con ese nombre, y si existe uno, también se verifican los permisos válidos de ejecución (+ x). Si alguna de esas verificaciones falla, el proceso pasa al siguiente componente. De lo contrario, el comando se resuelve en esta ruta y se realiza la búsqueda.
  4. Si te quedas sin componentes, la búsqueda falla.
  5. Si no hay nada PATHo no existe, haga lo que quiera.

Los shells reales tendrán comandos incorporados, que se encuentran antes de esta búsqueda, y a menudo también alias y funciones. Aquellos con los que no interactúan PATH. POSIX define algunos comportamientos en torno a ellos , y su shell puede tener mucho más.


Si bien es posible confiar en exec*hacer la mayor parte de esto por usted, el shell en la práctica puede implementar esta búsqueda en sí misma, especialmente para fines de almacenamiento en caché, pero el comportamiento de la memoria caché vacía debería ser similar. Los proyectiles tienen una latitud bastante amplia aquí y tienen comportamientos sutilmente diferentes en los casos de esquina.

Como descubrió, Bash usa una tabla hash para recordar las rutas completas de los comandos que se han visto antes, y se puede acceder a esa tabla con la hashfunción. La primera vez que ejecuta un comando, busca, y cuando se encuentra un resultado, se agrega a la tabla, por lo que no hay necesidad de molestarse en buscarlo la próxima vez que lo intente.

En zsh, por otro lado, PATHgeneralmente se busca el valor completo cuando se inicia el shell. Una tabla de búsqueda se rellena previamente con todos los nombres de comandos descubiertos, por lo que las búsquedas en tiempo de ejecución generalmente no son necesarias (a menos que se agregue un nuevo comando). Puede notar que esto sucede cuando intenta completar con tabulación un comando que no existía antes.

Los shells muy livianos, como dash, tienden a delegar la mayor cantidad de comportamiento posible a la biblioteca del sistema y no se molestan en recordar rutas de comandos pasadas.

Michael Homer
fuente
Muchas gracias por una explicación tan detallada, esto realmente da una visión profunda. ¡Tu comparación PATHentre bashy zshme ayuda a resolver mi confusión!
Xlee