Aqui está tudo o que você nunca pensou que nunca iria querer saber sobre isso:
Sumário
Para obter o nome do caminho de um executável em um script de shell semelhante ao Bourne (existem algumas ressalvas; veja abaixo):
ls=$(command -v ls)
Para descobrir se um determinado comando existe:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
No prompt de um shell interativo tipo Bourne:
type ls
O which
comando é uma herança quebrada do C-Shell e é melhor ficar sozinho em conchas semelhantes a Bourne.
Casos de Uso
Há uma distinção entre procurar essas informações como parte de um script ou interativamente no prompt do shell.
No prompt do shell, o caso de uso típico é: esse comando se comporta de maneira estranha, estou usando o correto? O que exatamente aconteceu quando eu digitei mycmd
? Posso olhar mais para o que é?
Nesse caso, você deseja saber o que seu shell faz quando invoca o comando sem realmente invocá-lo.
Nos scripts shell, ele tende a ser bem diferente. Em um script de shell, não há razão para você querer saber onde ou qual é o comando, se tudo o que você quer fazer é executá-lo. Geralmente, o que você deseja saber é o caminho do executável, para que você possa obter mais informações (como o caminho para outro arquivo relativo a ele ou ler informações do conteúdo do arquivo executável nesse caminho).
Interativamente, você pode querer saber sobre todos os my-cmd
comandos disponíveis no sistema, em scripts, raramente.
A maioria das ferramentas disponíveis (como costuma ser o caso) foram projetadas para serem usadas interativamente.
História
Um pouco da história primeiro.
O início do shell do Unix até o final dos anos 70 não tinha funções ou pseudônimos. Somente a procura tradicional de executáveis em $PATH
. csh
introduziu aliases por volta de 1978 (embora tenha csh
sido lançado pela primeira vez em 2BSD
maio de 1979), e também o processamento de um .cshrc
para os usuários personalizarem o shell (todo shell, como se csh
lê .cshrc
mesmo quando não é interativo, como nos scripts).
enquanto o shell Bourne foi lançado pela primeira vez no Unix V7 no início de 1979, o suporte a funções só foi adicionado muito mais tarde (1984 no SVR2) e, de qualquer forma, ele nunca teve algum rc
arquivo ( .profile
é para configurar seu ambiente, não o shell em si ).
csh
ficou muito mais popular que o shell Bourne porque (embora tivesse uma sintaxe muito pior que o shell Bourne), estava adicionando muitos recursos mais convenientes e agradáveis para uso interativo.
Em 3BSD
(1980), um which
script csh foi adicionado para que os csh
usuários ajudassem a identificar um executável, e é um script dificilmente diferente que você pode encontrar hoje which
em muitos Unices comerciais (como Solaris, HP / UX, AIX ou Tru64).
Esse script lê o usuário ~/.cshrc
(como todos os csh
scripts fazem, a menos que seja invocado com csh -f
) e consulta os nomes de comando fornecidos na lista de aliases e em $path
(a matriz csh
mantida com base em $PATH
).
Aqui está, which
primeiro lugar para o shell mais popular da época (e csh
ainda era popular até meados dos anos 90), que é a principal razão pela qual foi documentado em livros e ainda é amplamente utilizado.
Observe que, mesmo para um csh
usuário, esse which
script csh não fornece necessariamente as informações corretas. Ele obtém os aliases definidos em ~/.cshrc
, não os que você pode ter definido mais tarde no prompt ou, por exemplo, source
inserindo outro csh
arquivo e (embora isso não seja uma boa idéia), PATH
possa ser redefinido ~/.cshrc
.
A execução desse which
comando a partir de um shell Bourne ainda procuraria aliases definidos em seu diretório ~/.cshrc
, mas se não houver um porque você não o usa csh
, provavelmente ainda obterá a resposta certa.
Uma funcionalidade semelhante não foi adicionada ao shell Bourne até 1984 no SVR2 com o type
comando embutido. O fato de ele estar embutido (em oposição a um script externo) significa que ele pode fornecer as informações corretas (até certo ponto), pois ele tem acesso aos elementos internos do shell.
O type
comando inicial sofreu um problema semelhante ao do which
script, pois não retornou um status de saída com falha se o comando não foi encontrado. Além disso, para os executáveis, ao contrário which
, ele gera algo como, em ls is /bin/ls
vez de apenas o /bin/ls
que tornou menos fácil o uso em scripts.
O shell Bourne da versão 8 do Unix (não lançado na natureza) tinha o type
nome incorporado whatis
. E o shell do Plan9 (o futuro sucessor do Unix) rc
(e seus derivados como akanga
e es
) também tem whatis
.
O shell Korn (um subconjunto no qual a definição sh POSIX se baseia), desenvolvido em meados dos anos 80, mas não amplamente disponível antes de 1988, adicionou muitos dos csh
recursos (editor de linha, aliases ...) no topo do shell Bourne . Ele adicionou seu próprio whence
builtin (além de type
), que tomou várias opções ( -v
para fornecer a type
saída detalhada semelhante a, e -p
procurar apenas executáveis (não aliases / funções ...)).
Coincidentemente à turbulência com relação às questões de direitos autorais entre a AT&T e Berkeley, algumas implementações de shell de software livre surgiram no final dos anos 80 e início dos 90. Todo o shell Almquist (ash, para substituir o shell Bourne nos BSDs), a implementação de domínio público do ksh (pdksh) bash
(patrocinada pela FSF), zsh
foi lançada entre 1989 e 1991.
Ash, apesar de ser um substituto para o shell Bourne, não tinha um type
built-in até muito mais tarde (no NetBSD 1.3 e no FreeBSD 2.3), embora tivesse hash -v
. O OSF / 1 /bin/sh
tinha um type
built-in que sempre retornava 0 até OSF / 1 v3.x. bash
não whence
adicionou uma -p
opção, mas adicionou uma opção para type
imprimir o caminho ( type -p
seria como whence -p
) e -a
relatar todos os comandos correspondentes. tcsh
fez which
builtin e adicionou um where
comando agindo como bash
's type -a
. zsh
tem todos eles.
O fish
shell (2005) possui um type
comando implementado como uma função.
O which
roteiro csh entretanto foi removido do NetBSD (como era embutido no tcsh e de não muito uso em outras conchas), e a funcionalidade adicional para whereis
(quando invocado como which
, whereis
se comporta como which
exceto que ele só olha para cima executáveis em $PATH
). No OpenBSD e no FreeBSD, which
também foi alterado para um escrito em C que consulta $PATH
apenas os comandos .
Implementações
Existem dezenas de implementações de um which
comando em vários Unices com sintaxe e comportamento diferentes.
No Linux (além dos integrados no tcsh
e zsh
), encontramos várias implementações. Nos sistemas Debian recentes, por exemplo, é um simples shell script POSIX que procura por comandos $PATH
.
busybox
também tem um which
comando
Há uma GNU
which
que é provavelmente a mais extravagante. Ele tenta estender o que o which
script csh fez a outros shells: você pode dizer quais são seus apelidos e funções para que ele possa lhe dar uma resposta melhor (e acredito que algumas distribuições Linux definem alguns apelidos globais em torno disso para bash
fazer isso) .
zsh
possui alguns operadores para expandir para o caminho dos executáveis: o operador de =
expansão de nome de arquivo e o :c
modificador de expansão de histórico (aqui aplicado à expansão de parâmetro ):
$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls
zsh
, no zsh/parameters
módulo também cria a tabela de hash do comando como a commands
matriz associativa:
$ print -r -- $commands[ls]
/bin/ls
O whatis
utilitário (exceto aquele no shell Unix V8 Bourne ou no Plan 9 rc
/ es
) não é realmente relacionado, pois é apenas para documentação (mostra o banco de dados whatis, que é a sinopse da página de manual).
whereis
também foi adicionado 3BSD
ao mesmo tempo em which
que foi gravado e C
não csh
é usado para procurar ao mesmo tempo o executável, a página de manual e a fonte, mas não com base no ambiente atual. Então, novamente, isso responde a uma necessidade diferente.
Agora, na frente padrão, o POSIX especifica os comandos command -v
e -V
(que eram opcionais até o POSIX.2008). UNIX especifica o type
comando (sem opção). Isso é tudo ( where
, which
, whence
não são especificados em qualquer padrão)
Até algumas versões, type
e command -v
eram opcionais na especificação Linux Standard Base, o que explica por que, por exemplo, algumas versões antigas posh
(embora baseadas nas pdksh
que tinham as duas) também não tinham. command -v
também foi adicionado a algumas implementações de shell Bourne (como no Solaris).
Status Hoje
Atualmente, o status é esse type
e command -v
é onipresente em todas as conchas do tipo Bourne (embora, como observado por @jarno, observe a advertência / bug bash
quando não estiver no modo POSIX ou alguns descendentes do shell Almquist abaixo nos comentários). tcsh
é o único shell em que você gostaria de usar which
(como não type
existe e which
está embutido).
Nos outros do que conchas tcsh
e zsh
, which
pode dizer-lhe o caminho do executável dado enquanto não há nenhuma alias ou função por esse mesmo nome em qualquer um dos nossos ~/.cshrc
, ~/.bashrc
ou qualquer arquivo de inicialização shell e você não definir $PATH
a sua ~/.cshrc
. Se você tiver um alias ou função definido para ele, ele pode ou não falar sobre isso ou dizer a coisa errada.
Se você quiser saber sobre todos os comandos com um determinado nome, não há nada portátil. Você usaria where
em tcsh
ou zsh
, type -a
na bash
ou zsh
, whence -a
no ksh93 e em outros shells, você pode usar type
em combinação com which -a
o que pode trabalhar.
Recomendações
Obtendo o nome do caminho para um executável
Agora, para obter o nome do caminho de um executável em um script, existem algumas ressalvas:
ls=$(command -v ls)
seria a maneira padrão de fazê-lo.
Existem alguns problemas, porém:
- Não é possível conhecer o caminho do executável sem executá-lo. Todos os
type
, which
, command -v
... todas as heurísticas de uso para descobrir o caminho. Eles percorrem os $PATH
componentes e localizam o primeiro arquivo que não é de diretório para o qual você tem permissão de execução. No entanto, dependendo do shell, quando se trata de executar o comando, muitos deles (Bourne, AT&T ksh, zsh, ash ...) apenas os executam na ordem de $PATH
até que a execve
chamada do sistema não retorne com erro . Por exemplo, se $PATH
contém /foo:/bar
e você deseja executar ls
, eles primeiro tentarão executar /foo/ls
ou se isso falhar /bar/ls
. Agora execução de/foo/ls
pode falhar porque você não tem permissão de execução, mas também por muitos outros motivos, como se não fosse um executável válido. command -v ls
informaria /foo/ls
se você tiver permissão de execução /foo/ls
, mas a execução ls
pode realmente ser executada /bar/ls
se /foo/ls
não for um executável válido.
- if
foo
é um builtin ou função ou alias, command -v foo
retorna foo
. Com alguns shells como ash
, pdksh
ou zsh
, ele também pode retornar foo
se $PATH
incluir a string vazia e houver um foo
arquivo executável no diretório atual. Existem algumas circunstâncias em que você pode levar isso em consideração. Lembre-se, por exemplo, de que a lista de componentes internos varia com a implementação do shell (por exemplo, mount
às vezes é incorporada ao busybox sh
) e, por exemplo, bash
pode obter funções do ambiente.
- se
$PATH
contiver componentes de caminho relativo (normalmente .
ou a cadeia vazia que se refere ao diretório atual, mas pode ser qualquer coisa), dependendo do shell, command -v cmd
pode não gerar um caminho absoluto. Portanto, o caminho que você obtém no momento da execução command -v
não será mais válido depois de você cd
em outro lugar.
- Anedótica: com o shell ksh93, se
/opt/ast/bin
(embora esse caminho exato pode variar em diferentes sistemas creio eu) está em você $PATH
, ksh93 vai disponibilizar algumas builtins extras ( chmod
, cmp
, cat
...), mas command -v chmod
vai voltar /opt/ast/bin/chmod
mesmo se esse caminho doesn' não existe.
Determinando se um Comando Existe
Para descobrir se um determinado comando existe de maneira padrão, você pode:
if command -v given-command > /dev/null 2>&1; then
echo given-command is available
else
echo given-command is not available
fi
Onde alguém pode querer usar which
(t)csh
Em csh
e tcsh
você não tem muita escolha. Em tcsh
, tudo bem como which
está embutido. Em csh
, esse será o which
comando do sistema , que pode não fazer o que você deseja em alguns casos.
encontre comandos apenas em algumas conchas
Um caso em que pode fazer sentido usar which
é se você quer saber o caminho de um comando, ignorando builtins shell potenciais ou funções em bash
, csh
(não tcsh
), dash
ou Bourne
shell scripts que é conchas que não têm whence -p
(como ksh
ou zsh
) , command -ev
(like yash
), whatis -p
( rc
, akanga
) ou um builtin which
(like tcsh
ou zsh
) em sistemas onde which
está disponível e não é o csh
script.
Se essas condições forem atendidas, então:
echo=$(which echo)
forneceria o caminho do primeiro echo
em $PATH
(exceto nos casos de canto), independentemente de ser echo
também um shell builtin / alias / function ou não.
Em outras conchas, você prefere:
- zsh :
echo==echo
ou echo=$commands[echo]
ouecho=${${:-echo}:c}
- ksh , zsh :
echo=$(whence -p echo)
- yash :
echo=$(command -ev echo)
- rc , akanga :
echo=`whatis -p echo`
(cuidado com os caminhos com espaços)
- peixe :
set echo (type -fp echo)
Observe que, se tudo o que você quer fazer é executar esse echo
comando, você não precisa obter o caminho, basta:
env echo this is not echoed by the builtin echo
Por exemplo, com tcsh
, para impedir que o builtin which
seja usado:
set Echo = "`env which echo`"
quando você precisa de um comando externo
Outro caso em que você pode querer usar which
é quando você realmente precisa de um comando externo. O POSIX exige que todos os recursos internos do shell (como command
) também estejam disponíveis como comandos externos, mas infelizmente esse não é o caso command
em muitos sistemas. Por exemplo, é raro encontrar um command
comando em sistemas operacionais baseados em Linux, enquanto a maioria deles possui um which
comando (embora diferentes, com opções e comportamentos diferentes).
Os casos em que você pode querer um comando externo estão onde quer que você execute um comando sem chamar um shell POSIX.
As funções system("some command line")
, popen()
... do C ou de vários idiomas chamam um shell para analisar essa linha de comando, assim system("command -v my-cmd")
como o trabalho neles. Uma exceção a isso seria perl
otimizar o shell se ele não vir nenhum caractere especial do shell (além do espaço). Isso também se aplica ao seu operador de backtick:
$ perl -le 'print system "command -v emacs"'
-1
$ perl -le 'print system ":;command -v emacs"'
/usr/bin/emacs
0
$ perl -e 'print `command -v emacs`'
$ perl -e 'print `:;command -v emacs`'
/usr/bin/emacs
A adição disso :;
acima força perl
a invocar uma concha lá. Ao usar which
, você não precisaria usar esse truque.
which
está assumindo um contexto de shell interativo. Esta pergunta está marcada / portabilidade. Então, eu interpreto a pergunta neste contexto como "o que usar em vez dewhich
encontrar o primeiro executável de um determinado nome no$PATH
". A maioria das respostas e razões contrawhich
lidar com aliases, buildins e funções, que na maioria dos scripts de shell portáteis do mundo real são apenas de interesse acadêmico. Aliases definidos localmente não são herdados ao executar um script de shell (a menos que você o forneça.
).