Como você suspeita, o comportamento exato depende do shell, mas um nível básico de funcionalidade é especificado pelo POSIX.
A pesquisa e execução de comandos para a linguagem de comando padrão do shell (na qual a maioria dos shells implementam um superconjunto) têm muitos casos, mas estamos interessados apenas no momento no caso em que PATH
é usado. Nesse caso:
o comando deve ser procurado usando a variável de ambiente PATH conforme descrito em Variáveis de ambiente XBD
e
Se a pesquisa for bem sucedida:
[...]
o shell executa o utilitário em um ambiente de utilitário separado, com ações equivalentes a chamar a execl()
função [...] com o argumento de caminho definido como o nome do caminho resultante da pesquisa.
No caso malsucedido, a execução falha e um código de saída 127 é retornado com uma mensagem de erro.
Esse comportamento é consistente com a execvp
função, em particular. Todas as exec*
funções aceitam o nome do arquivo de um programa a ser executado, uma sequência de argumentos (que será argv
o programa) e talvez um conjunto de variáveis de ambiente. Para as versões usando a PATH
pesquisa, o POSIX define que :
O arquivo de argumento é usado para construir um nome de caminho que identifica o novo arquivo de imagem de processo. O [...] prefixo do caminho para esse arquivo é obtido por uma pesquisa nos diretórios passados como a variável de ambiente PATH
O comportamento do PATH é definido em outro lugar como:
Essa variável deve representar a sequência de prefixos de caminho que certas funções e utilitários aplicam na busca de um arquivo executável conhecido apenas por um nome de arquivo. Os prefixos devem ser separados por um <colon> (':'). Quando um prefixo de tamanho diferente de zero é aplicado a esse nome de arquivo, um <slash> deve ser inserido entre o prefixo e o nome do arquivo se o prefixo não terminar. Um prefixo de comprimento zero é um recurso herdado que indica o diretório de trabalho atual. Ele aparece como dois caracteres adjacentes ("::"), como um <colon> inicial que precede o restante da lista ou como um <colon> à direita após o restante da lista. Um aplicativo estritamente conforme deve usar um nome de caminho real (como.) Para representar o diretório de trabalho atual no PATH.A lista deve ser pesquisada do início ao fim, aplicando o nome do arquivo a cada prefixo, até que um arquivo executável com o nome especificado e as permissões de execução apropriadas seja encontrado . Se o nome do caminho que está sendo procurado contiver um <slash>, a pesquisa pelos prefixos do caminho não deverá ser realizada. Se o nome do caminho começar com um <slash>, o caminho especificado será resolvido (consulte Resolução do nome do caminho ). Se PATH estiver desativado ou definido como nulo, a pesquisa de caminho será definida pela implementação.
Isso é um pouco denso, então um resumo:
- Se o nome do programa tiver um
/
(barra, U + 002F SOLIDUS), trate-o como um caminho da maneira usual e pule o restante deste processo. Para o shell, esse caso tecnicamente não surge (porque as regras do shell já terão lidado com ele).
- O valor de
PATH
é dividido em partes em cada dois pontos e, em seguida, cada componente é processado da esquerda para a direita. Como um caso especial (histórico), um componente vazio de uma variável não vazia é tratado como .
(o diretório atual).
- Para cada componente, o nome do programa é anexado ao final com uma junção
/
e a existência de um arquivo com esse nome é verificada e, se existir, as permissões de execução (+ x) válidas também serão verificadas. Se uma dessas verificações falhar, o processo passará para o próximo componente. Caso contrário, o comando será resolvido para esse caminho e a pesquisa será concluída.
- Se você ficar sem componentes, a pesquisa falhará.
- Se não houver nada
PATH
, ou ele não existir, faça o que quiser.
Os shells reais terão comandos integrados, que são encontrados antes dessa pesquisa, e frequentemente aliases e funções também. Aqueles não interagem com PATH
. O POSIX define algum comportamento em torno deles , e seu shell pode ter muito mais.
Embora seja possível confiar na exec*
maior parte disso para você, o shell na prática pode implementar essa pesquisa em si, principalmente para fins de armazenamento em cache, mas o comportamento do cache vazio deve ser semelhante. Os shells têm latitude bastante ampla aqui e comportamentos sutilmente diferentes nos casos de canto.
Como você descobriu, o Bash usa uma tabela de hash para lembrar os caminhos completos dos comandos vistos anteriormente, e essa tabela pode ser acessada com a hash
função Na primeira vez em que você executa um comando, ele pesquisa e, quando um resultado é encontrado, ele é adicionado à tabela, para que você não precise se preocupar em procurar na próxima vez que tentar.
No zsh, por outro lado, o total PATH
geralmente é pesquisado quando o shell é iniciado. Uma tabela de pesquisa é pré-preenchida com todos os nomes de comandos descobertos, de modo que as pesquisas em tempo de execução geralmente não são necessárias (a menos que um novo comando seja adicionado). Você pode perceber isso acontecendo ao tentar tab-complete um comando que não existia antes.
Cascas muito leves, por exemplo dash
, tendem a delegar o máximo de comportamento possível à biblioteca do sistema e não se preocupam em se lembrar dos caminhos de comando anteriores.
PATH
entrebash
ezsh
me ajuda a resolver minha confusão!