Como localizo com segurança o caminho completo de um programa no PATH?

12

Eu preciso encontrar o caminho de um determinado programa no PATHusando um script de shell. O caminho deve ser o caminho completo real do programa, que pode ser passado posteriormente para uma das exec*funções que não pesquisa por PATHsi só, por exemplo execv.

Existem programas como o killque estão disponíveis como um programa real e um shell embutido ao mesmo tempo. Se for esse o caso, preciso do caminho completo para o programa real.

Existem vários utilitários que podem encontrar um programa PATHconforme especificado na Seção 2.9.1.1, Pesquisa de comando e execução do padrão POSIX .

Existe which, o que não faz parte de nenhum padrão. Pode ser um programa regular em alguns sistemas, enquanto alguns shells fornecem que é um componente interno. Parece estar disponível na maioria dos sistemas e shells, mas os shells com uma versão interna também retornam apenas o nome do built-in em vez do caminho para o executável. Também não é padronizado de forma alguma e pode retornar qualquer saída e ter opções diferentes.

bash# which kill
/usr/bin/kill
dash# which kill
/usr/bin/kill
fish# which kill
/usr/bin/kill
mksh# which kill
/usr/bin/kill
tcsh# which kill
kill: shell built-in command.
zsh# which kill
kill: shell built-in command

Existe whence, que é um built-in de algumas conchas. Mas não está disponível em muitas conchas. Também retornará o nome do built-in em vez do caminho para o programa. A -ppode ser passado para onde alterar esse comportamento.

bash# whence kill
bash: whence: command not found
dash# whence kill
dash: 1: whence: not found
fish# whence kill
fish: Unknown command 'whence'
mksh# whence kill
kill
mksh# whence -p kill
/usr/bin/kill
tcsh# whence kill
whence: Command not found.
zsh# whence kill
kill
zsh# whence -p kill
/usr/bin/kill

Existe o commandbuilt-in especificado por POSIX: 2008 . Infelizmente, ele também procura comandos regulares e embutidos e retornará o nome do embutido em vez do caminho para o programa sombreado por um embutido com o mesmo nome. Algumas conchas antigas ainda não foram implementadas.

bash# command -v kill
kill
dash# command -v kill
kill
fish# command -v kill
/usr/bin/kill
mksh# command -v kill
kill
tcsh# command -v kill
command: Command not found.
zsh# command -v kill
kill
Sebastian Schrader
fonte
Não consigo descobrir se enableestá especificado no POSIX ou não, mas se estiver, você pode usar enable -n whichpara desativar o shell interno do which.
Muzer #
e existerealpath
Ipor Sircer
@Muzer Nas conchas que tenho à minha disposição, enableé fornecido apenas por bashezsh
Sebastian Schrader
1
Você precisa de um método confiável para o shell específico que está executando seu script, não para todos os shells. Os scripts não são executados por um shell aleatório, mas especificamente pelo shell especificado na linha shebang. Dito isto, em bash que seria type -p. Tanto o bash quanto o dash permitem dizer o commandcomando para executar um executável real, mesmo se houver uma função ou um builtin com o mesmo nome.
AlexP #
1
O @AlexP commandignora funções (e aliases), mas NÃO está embutido, como o Q diz corretamente. E você nem sempre pode usar um shebang porque não há caminho que obtenha um shell específico, ou mesmo algum shell POSIX, em todos os sistemas.
David_thompson_085

Respostas:

11

Basta procurar por si mesmo.

export IFS=":"
[ -z "${1}" ] && exit 1
for dir in $PATH
do if [ -x "${dir}/${1}" ]
   then echo "${dir}/${1}"
        exit 0
   fi
done
echo ${1} not found
exit 1

Testado em bash, dash, ksh, mksh,zsh

Atualizar

O acima é bom para um script independente, no entanto, se você planeja incorporá-lo a um script maior, pode usar algo mais parecido com o seguinte.

function find_path() {
   IFS_SAVE="${IFS}"
   export IFS=":"
   [ -z "${1}" ] && exit 1
   for dir in $PATH
   do if [ -x "${dir}/${1}" ]
      then echo "${dir}/${1}"
           export IFS="${IFS_SAVE}"
           return 0
      fi
   done
   export IFS="${IFS_SAVE}"
   echo ${1} not found
   return 1
}

Isso é para que IFSé restaurada depois de encontrar o jogo, também trocadas exit's com return' s

Zachary Brady
fonte
1
Talvez -x em vez de -f?
Jeff Schaller
@JeffSchaller bom ponto, não há razão para escolher arquivos não executáveis.
Zachary Brady
3
Se você integrar isso a um script shell maior (em vez de transformá-lo em um script por si só, como é supostamente pretendido), convém restaurar o valor antigo do IFS posteriormente - caso contrário, isso pode ter muitos efeitos sobre o resto do script ...
psmears 17/11
Por que exportar a IFSvariável? Não é suficiente ter esse conjunto no shell local? Falando em local, seria local IFSportátil? O acima pode interagir mal se algo estiver salvando o IFS da mesma maneira. Olhando para esta pergunta no SE , localpode funcionar para a maioria dos shells, apesar de não ser POSIX. Colocar a versão original em um (…)subshell também pode funcionar.
MvG