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 whichcomando é 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-cmdcomandos 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. cshintroduziu aliases por volta de 1978 (embora tenha cshsido lançado pela primeira vez em 2BSDmaio de 1979), e também o processamento de um .cshrcpara os usuários personalizarem o shell (todo shell, como se cshlê .cshrcmesmo 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 rcarquivo ( .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 whichscript csh foi adicionado para que os cshusuários ajudassem a identificar um executável, e é um script dificilmente diferente que você pode encontrar hoje whichem muitos Unices comerciais (como Solaris, HP / UX, AIX ou Tru64).
Esse script lê o usuário ~/.cshrc(como todos os cshscripts 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 cshmantida com base em $PATH).
Aqui está, whichprimeiro lugar para o shell mais popular da época (e cshainda 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 cshusuário, esse whichscript 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, sourceinserindo outro csharquivo e (embora isso não seja uma boa idéia), PATHpossa ser redefinido ~/.cshrc.
A execução desse whichcomando 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 typecomando 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 typecomando inicial sofreu um problema semelhante ao do whichscript, 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/lsvez de apenas o /bin/lsque 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 typenome incorporado whatis. E o shell do Plan9 (o futuro sucessor do Unix) rc(e seus derivados como akangae 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 cshrecursos (editor de linha, aliases ...) no topo do shell Bourne . Ele adicionou seu próprio whencebuiltin (além de type), que tomou várias opções ( -vpara fornecer a typesaída detalhada semelhante a, e -pprocurar 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), zshfoi lançada entre 1989 e 1991.
Ash, apesar de ser um substituto para o shell Bourne, não tinha um typebuilt-in até muito mais tarde (no NetBSD 1.3 e no FreeBSD 2.3), embora tivesse hash -v. O OSF / 1 /bin/shtinha um typebuilt-in que sempre retornava 0 até OSF / 1 v3.x. bashnão whenceadicionou uma -popção, mas adicionou uma opção para typeimprimir o caminho ( type -pseria como whence -p) e -arelatar todos os comandos correspondentes. tcshfez whichbuiltin e adicionou um wherecomando agindo como bash's type -a. zshtem todos eles.
O fishshell (2005) possui um typecomando implementado como uma função.
O whichroteiro 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, whereisse comporta como whichexceto que ele só olha para cima executáveis em $PATH). No OpenBSD e no FreeBSD, whichtambém foi alterado para um escrito em C que consulta $PATHapenas os comandos .
Implementações
Existem dezenas de implementações de um whichcomando em vários Unices com sintaxe e comportamento diferentes.
No Linux (além dos integrados no tcshe zsh), encontramos várias implementações. Nos sistemas Debian recentes, por exemplo, é um simples shell script POSIX que procura por comandos $PATH.
busyboxtambém tem um whichcomando
Há uma GNU whichque é provavelmente a mais extravagante. Ele tenta estender o que o whichscript 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 bashfazer isso) .
zshpossui alguns operadores para expandir para o caminho dos executáveis: o operador de = expansão de nome de arquivo e o :cmodificador 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/parametersmódulo também cria a tabela de hash do comando como a commandsmatriz associativa:
$ print -r -- $commands[ls]
/bin/ls
O whatisutilitá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).
whereistambém foi adicionado 3BSDao mesmo tempo em whichque foi gravado e Cnã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 -ve -V(que eram opcionais até o POSIX.2008). UNIX especifica o typecomando (sem opção). Isso é tudo ( where, which, whencenão são especificados em qualquer padrão)
Até algumas versões, typee command -veram opcionais na especificação Linux Standard Base, o que explica por que, por exemplo, algumas versões antigas posh(embora baseadas nas pdkshque tinham as duas) também não tinham. command -vtambém foi adicionado a algumas implementações de shell Bourne (como no Solaris).
Status Hoje
Atualmente, o status é esse typee command -vé onipresente em todas as conchas do tipo Bourne (embora, como observado por @jarno, observe a advertência / bug bashquando 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 typeexiste e whichestá embutido).
Nos outros do que conchas tcshe zsh, whichpode 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, ~/.bashrcou qualquer arquivo de inicialização shell e você não definir $PATHa 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 whereem tcshou zsh, type -ana bashou zsh, whence -ano ksh93 e em outros shells, você pode usar typeem combinação com which -ao 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 $PATHcomponentes 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 $PATHaté que a execvechamada do sistema não retorne com erro . Por exemplo, se $PATHcontém /foo:/bare você deseja executar ls, eles primeiro tentarão executar /foo/lsou se isso falhar /bar/ls. Agora execução de/foo/lspode 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 lsinformaria /foo/lsse você tiver permissão de execução /foo/ls, mas a execução lspode realmente ser executada /bar/lsse /foo/lsnão for um executável válido.
- if
fooé um builtin ou função ou alias, command -v fooretorna foo. Com alguns shells como ash, pdkshou zsh, ele também pode retornar foose $PATHincluir a string vazia e houver um fooarquivo 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, bashpode obter funções do ambiente.
- se
$PATHcontiver 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 cmdpode não gerar um caminho absoluto. Portanto, o caminho que você obtém no momento da execução command -vnão será mais válido depois de você cdem 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 chmodvai voltar /opt/ast/bin/chmodmesmo 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 cshe tcshvocê não tem muita escolha. Em tcsh, tudo bem como whichestá embutido. Em csh, esse será o whichcomando 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), dashou Bourneshell scripts que é conchas que não têm whence -p(como kshou zsh) , command -ev(like yash), whatis -p( rc, akanga) ou um builtin which(like tcshou zsh) em sistemas onde whichestá disponível e não é o cshscript.
Se essas condições forem atendidas, então:
echo=$(which echo)
forneceria o caminho do primeiro echoem $PATH(exceto nos casos de canto), independentemente de ser echotambém um shell builtin / alias / function ou não.
Em outras conchas, você prefere:
- zsh :
echo==echoou 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 echocomando, 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 whichseja 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 commandem muitos sistemas. Por exemplo, é raro encontrar um commandcomando em sistemas operacionais baseados em Linux, enquanto a maioria deles possui um whichcomando (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 perlotimizar 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 perla invocar uma concha lá. Ao usar which, você não precisaria usar esse truque.
whichestá 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 dewhichencontrar o primeiro executável de um determinado nome no$PATH". A maioria das respostas e razões contrawhichlidar 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.).