Come funziona la ricerca in $ PATH sotto il cofano?


8

Ci sono troppi articoli / risorse sul web che insegnano alle persone come impostare la variabile d'ambiente in PATHmodo che possano usare la scorciatoia javao pythonetc invece del percorso assoluto nell'interfaccia della riga di comando.

Quello che mi interessa sapere è che cosa c'è dietro la scena quando digitiamo il comando e premiamo invio (simile a ciò che accade quando si digita un URL nel browser ).

Ecco la mia ipotesi:

  1. leggi il comando (analizza / preelabora stdin per ottenere gli argomenti giusti $@)
  2. ricerca comandi
  3. esecuzione del comando (programma avviato, consuma memoria, stdout / stderr alla shell)
  4. eseguire nuovamente il rendering dell'emulatore in base alle variabili di ambiente pertinenti (ad es $PS#. $PROMPT, ecc.)

La parte che voglio capire di più è la ricerca dei comandi. Ovviamente, $PATHviene consumato da alcune funzioni in background e separato da :/ ;come delimitatori, quindi cosa è successo? Usiamo una tabella hash (chiave: basename del file, valore: dirname assoluto del file) per archiviare i file binari in quei PERCORSI o altri hook?

NOTA: inizialmente pensavo che fosse una tabella hash come posso usare [ -z hash [command] ]per verificare se un comando è disponibile nell'env corrente, ma quando uso hash | grep pythonnon ottengo nulla dall'output mentre which pythonfunziona come previsto. (Penso che il meccanismo potrebbe essere specifico della shell, ma voglio ottenere maggiori approfondimenti al riguardo.)

Risposte:


11

Come sospetti, il comportamento esatto dipende dalla shell, ma POSIX specifica un livello di base della funzionalità.

La ricerca e l'esecuzione dei comandi per il linguaggio di comandi della shell standard (di cui la maggior parte delle shell implementa un superset) presentano molti casi, ma per il momento siamo interessati solo al caso in cui PATHviene utilizzato. In quel caso:

il comando deve essere cercato usando la variabile d'ambiente PATH come descritto in Variabili d'ambiente XBD

e

Se la ricerca ha esito positivo:

[...]

la shell esegue l'utilità in un ambiente di utilità separato con azioni equivalenti a chiamare la execl()funzione [...] con l' argomento path impostato sul nome percorso risultante dalla ricerca.

In caso di esito negativo, l'esecuzione non riesce e viene restituito un codice di uscita di 127 con un messaggio di errore.

Questo comportamento è coerente con la execvpfunzione, in particolare. Tutte le exec*funzioni accettano il nome del file di un programma da eseguire, una sequenza di argomenti (che sarà quella argvdel programma) e forse un insieme di variabili d'ambiente. Per le versioni che utilizzano la PATHricerca, POSIX definisce che :

Il file argomento viene utilizzato per costruire un nome percorso che identifica il nuovo file immagine di processo [...] il prefisso percorso per questo file è ottenuto da una ricerca delle directory passate come variabile d'ambiente PATH


Il comportamento di PATH è definito altrove come:

Questa variabile deve rappresentare la sequenza di prefissi di percorso che determinate funzioni e utilità si applicano nella ricerca di un file eseguibile noto solo con un nome file. I prefissi devono essere separati da un <colon> (':'). Quando viene applicato un prefisso di lunghezza diversa da zero a questo nome file, viene inserito un <slash> tra il prefisso e il nome file se il prefisso non termina. Un prefisso di lunghezza zero è una funzionalità legacy che indica la directory di lavoro corrente. Viene visualizzato come due caratteri adiacenti ("::"), come <colon> iniziale che precede il resto dell'elenco o come <colon> finale che segue il resto dell'elenco. Un'applicazione strettamente conforme deve utilizzare un percorso effettivo (come.) Per rappresentare l'attuale directory di lavoro in PERCORSO.L'elenco deve essere cercato dall'inizio alla fine, applicando il nome file a ciascun prefisso, fino a quando non viene trovato un file eseguibile con il nome specificato e le autorizzazioni di esecuzione appropriate . Se il nome del percorso cercato contiene un <slash>, la ricerca attraverso i prefissi del percorso non deve essere eseguita. Se il percorso inizia con una <slash>, il percorso specificato viene risolto (vedere Risoluzione del nome percorso ). Se PATH non è impostato o è impostato su null, la ricerca del percorso è definita dall'implementazione.

È un po 'denso, quindi un riassunto:

  1. Se il nome del programma contiene una /(barra, U + 002F SOLIDUS), trattalo come un percorso come di consueto e salta il resto di questo processo. Per la shell, questo caso tecnicamente non si presenta (perché le regole della shell lo avranno già risolto).
  2. Il valore di PATHviene diviso in pezzi su ciascun colon, quindi ciascun componente viene elaborato da sinistra a destra. Come un caso speciale (storico), un componente vuoto di una variabile non vuota viene trattato come .(la directory corrente).
  3. Per ogni componente, il nome del programma viene aggiunto alla fine con un join /e viene verificata l'esistenza di un file con quel nome, e se ne esiste uno vengono verificate anche le autorizzazioni di esecuzione (+ x) valide. Se uno di questi controlli fallisce, il processo passa al componente successivo. Altrimenti, il comando si risolve in questo percorso e la ricerca viene eseguita.
  4. Se si esauriscono i componenti, la ricerca ha esito negativo.
  5. Se non c'è niente dentro PATHo non esiste, fai quello che vuoi.

Le shell reali avranno comandi incorporati, che si trovano prima di questa ricerca, e spesso anche alias e funzioni. Quelli non interagiscono con PATH. POSIX definisce alcuni comportamenti attorno a quelli e la tua shell potrebbe avere molto di più.


Mentre è possibile fare affidamento exec*per fare la maggior parte di questo per te, la shell in pratica può implementare questa ricerca stessa, in particolare per scopi di cache, ma il comportamento della cache vuota dovrebbe essere simile. Le conchiglie hanno una latitudine abbastanza ampia qui e hanno comportamenti leggermente diversi nei casi d'angolo.

Come hai scoperto, Bash usa una tabella hash per ricordare i percorsi completi dei comandi che ha visto prima, e quella tabella è accessibile con la hashfunzione. La prima volta che esegui un comando cerca, e quando viene trovato un risultato viene aggiunto alla tabella, quindi non c'è bisogno di preoccuparsi di guardare la prossima volta che lo provi.

In zsh, d'altra parte, il full PATHviene generalmente cercato all'avvio della shell. Una tabella di ricerca è prepopolata con tutti i nomi dei comandi rilevati in modo che le ricerche di runtime di solito non siano necessarie (a meno che non venga aggiunto un nuovo comando). È possibile notare che ciò accade quando si tenta di completare un comando di tabulazione che prima non esisteva.

Le shell molto leggere, come quelle dash, tendono a delegare il maggior numero possibile di comportamenti alla libreria di sistema e non si preoccupano di ricordare i percorsi di comando passati.


Grazie mille per una spiegazione così dettagliata, questo dà davvero intuizioni profonde. Il tuo confronto PATHtra bashe zshmi aiuta a risolvere la mia confusione!
Xlee,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.