Ao procurar o caminho para um executável ou verificar o que aconteceria se você inserisse um nome de comando em um shell Unix, “há uma infinidade de utilitários diferentes ( which
, type
, command
, whence
, where
, whereis
, whatis
, hash
, etc).
Muitas vezes ouvimos que which
deve ser evitado. Por quê? O que devemos usar no lugar?
Comentários
Resposta
Aqui está tudo o que você nunca pensou que não gostaria de saber sobre isso:
Resumo
Para obter o nome do caminho de um executável em um script de shell do tipo Bourne (há 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 deixar sozinho em shells tipo Bourne.
Casos de uso
Lá “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 é: este comando se comporta de maneira estranha, estou usando o certo? O que aconteceu exatamente quando digitei mycmd
? Posso ver mais a fundo o que é?
Nesse caso, você quer saber o que seu shell faz quando você invoca o comando sem realmente invocar o comando.
Em scripts de shell, tende a ser bem diferente. Em um script de shell, não há razão para você querer saber onde ou o que é um comando se tudo o que você deseja fazer é executá-lo. Geralmente, o que você deseja saber é o caminho do executável, para que possa obter mais informações dele (como o caminho para outro arquivo relativo a esse, 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) foi projetada para ser usada interativamente.
História
Um pouco de história primeiro.
Os primeiros shells Unix até o final dos anos 70 não tinham funções ou apelidos. Apenas a pesquisa tradicional de executáveis em $PATH
. csh
introduziu aliases por volta de 1978 (embora csh
tenha sido lançado pela primeira vez em 2BSD
, em maio de 1979), e também o processamento de um .cshrc
para que os usuários personalizem o shell (cada shell, como csh
, lê .cshrc
mesmo quando não é interativo como em scripts).
Embora o shell Bourne tenha sido lançado no Unix V7 no início de 1979, o suporte a funções foi adicionado muito mais tarde (1984 em SVR2), e de qualquer forma, nunca teve algum arquivo rc
(o .profile
é para configurar seu ambiente, não o shell per se ).
csh
se tornou muito mais popular do que o shell Bourne (embora tivesse uma sintaxe terrivelmente pior do que o Bourne shell) estava adicionando muitos recursos mais convenientes e agradáveis para uso interativo.
Em 3BSD
(1980), um which
script csh foi adicionado para csh
usuários para ajudar a identificar um executável, e é um script dificilmente diferente que você pode encontrar como which
em muitos Unices comerciais hoje em dia (como Solaris, HP / UX, AIX ou Tru64).
Esse script lê o usuário” s ~/.cshrc
(como todos os csh
scripts fazem, a menos que invocados com csh -f
) e procura o (s) nome (s) de comando fornecido (s) na lista de aliases e em $path
(a matriz que csh
mantém com base em $PATH
).
Aqui está: which
veio em primeiro lugar para o shell mais popular da época (e csh
ainda era popular até meados de Anos 90), que é o principal motivo pelo qual foi documentado em livros e ainda é amplamente usado.
Observe que, mesmo para um usuário csh
, que which
o script csh não fornece necessariamente a informação certa. Ele obtém os aliases definidos em ~/.cshrc
, não aqueles que você pode ter definido posteriormente no prompt ou, por exemplo, source
selecionando outro csh
arquivo e (embora isso não seja uma boa ideia), PATH
pode ser redefinido em ~/.cshrc
.
Executar esse comando which
em um shell Bourne ainda procuraria aliases definidos em seu ~/.cshrc
, mas se você não tiver um porque não usa csh
, provavelmente ainda obterá a resposta certa.
Uma funcionalidade semelhante não foi adicionada ao o shell Bourne até 1984 em SVR2 com o comando interno type
. O fato de ser integrado (em oposição a um script externo) significa que pode fornecer as informações corretas (até certo ponto), pois tem acesso às partes internas do shell.
O comando type
inicial sofreu um problema semelhante ao script which
em que não retornou um status de saída de falha se o comando não foi encontrado. Além disso, para executáveis, ao contrário de which
, ele produz algo como ls is /bin/ls
em vez de apenas /bin/ls
o que o tornava menos fácil de usar em scripts.
Unix versão 8″ s (não liberado totalmente) O shell Bourne tinha “s type
builtin renomeado para whatis
. E o shell Plan9 (futuro sucessor do Unix) rc
(e seus derivados como akanga
e es
) têm whatis
também.
O shell Korn (um subconjunto do qual o POSIX sh
definição é baseada em), desenvolvido em meados dos anos 80, mas não amplamente disponível antes de 1988, adicionou muitos dos csh
recursos ( editor de linha, aliases …) na parte superior do shell Bourne. Adicionou seu próprio whence
integrado (além de type
) que exigia várias opções (-v
para fornecer a type
saída detalhada e -p
para procurar apenas executáveis (não aliases / funções …)) .
Coincidentemente com a turbulência com relação aos problemas de direitos autorais entre AT & T e Berkeley, algumas implementações de shell de software livre vieram no final dos anos 80, início dos anos 90.Todo o shell Almquist (ash
, para ser a substituição do shell Bourne em BSDs), a implementação de domínio público de ksh
(pdksh
), bash
(patrocinado pela FSF), zsh
foi lançado entre 1989 e 1991.
Ash, embora pretendesse ser um substituto para o shell Bourne, não tinha um type
integrado até muito mais tarde (no NetBSD 1.3 e no FreeBSD 2.3 ), embora tivesse hash -v
. OSF / 1 /bin/sh
tinha um type
integrado que sempre retornou 0 até OSF / 1 v3.x. bash
não “t adicionou um whence
mas adicionou um -p
opção para type
para imprimir o caminho (type -p
seria como whence -p
) e -a
para relatar todos os comandos correspondentes. tcsh
fez which
integrado e adicionou um where
comando agindo como bash
” s type -a
. zsh
tem todos eles.
O fish
shell (2005) tem um comando type
implementado como uma função.
O which
O script csh entretanto foi removido do NetBSD (visto que era embutido no tcsh e não era muito útil em outros shells), e a funcionalidade adicionada a whereis
(quando chamado como which
, whereis
se comporta como which
exceto que só procura executáveis em $PATH
). No OpenBSD e no FreeBSD, which
também foi alterado para um escrito em C que procura comandos em $PATH
apenas .
Implementações
Existem dezenas de implementações de um which
co mmand em vários Unices com sintaxe e comportamento diferentes.
No Linux (além dos integrados em tcsh
e zsh
) encontramos várias implementações. Em sistemas Debian recentes, por exemplo, é “um script de shell POSIX simples que procura comandos em $PATH
.
busybox
também tem um comando which
.
Há um GNU
which
que é provavelmente o mais extravagante. Ele tenta estender o que o which
script csh fez para outros shells: você pode dizer quais são seus apelidos e funções para que ele possa fornecer uma resposta melhor (e eu acredito que algumas distribuições Linux definem alguns aliases globais em torno disso para bash
fazer isso).
zsh
tem alguns operadores para expandir para o caminho dos executáveis: o operador =
expansão do nome do arquivo e o :c
modificador de expansão de histórico (aqui aplicado a expansão de parâmetro ):
$ print -r -- =ls /bin/ls $ cmd=ls; print -r -- $cmd:c /bin/ls
zsh
, no também cria a tabela de hash do comando como a commands
matriz associativa:
$ print -r -- $commands[ls] /bin/ls
O utilitário whatis
(exceto aquele no shell Unix V8 Bourne ou Plan 9 rc
/ es
) não está relacionado, pois “é apenas para documentação (greps o banco de dados whatis, que é a sinopse da página de manual”).
whereis
também foi adicionado em 3BSD
ao mesmo tempo que which
embora tenha sido escrito em C
, não csh
e é usado para pesquisar, ao mesmo tempo, o executável, a página do manual e o código-fonte, mas não com base no ambiente atual. Então, novamente, isso atende a uma necessidade diferente.
Agora, na frente padrão, POSIX especifica command -v
e -V
comandos (que costumavam ser opcionais até POSIX.2008). O UNIX especifica o comando type
(sem opção). Isso “é tudo (where
, which
, whence
não são especificados em nenhum padrão) .
Até algumas versões, type
e command -v
eram opcionais na especificação Linux Standard Base, o que explica por que para Por exemplo, algumas versões antigas de posh
(embora com base em pdksh
que tinha ambos) não tinham nenhum. command -v
também foi adicionado a algumas implementações de shell Bourne (como no Solaris).
Status hoje
O status hoje em dia é que type
e command -v
são onipresentes em todos os shells tipo Bourne (embora, conforme observado por @jarno, observe a advertência / bug em 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ê deseja usar which
(já que não há type
lá e which
está embutido).
Nos shells diferentes de tcsh
e zsh
, which
pode informar o caminho do executável fornecido, desde que não haja apelido ou função com o mesmo nome em qualquer um de nossos ~/.cshrc
, ~/.bashrc
ou qualquer arquivo de inicialização do shell e você não “t define $PATH
em seu ~/.cshrc
. Se você tiver um alias ou função definida para ele, ele pode ou não informar sobre isso, ou informar a você a coisa errada.
Se você quiser saber sobre todos os comandos de um determinado nome, não há nada portátil. Você “d usaria where
em tcsh
ou zsh
, type -a
em bash
ou zsh
, whence -a
em ksh93 e em outros shells , você pode usar type
em combinação com which -a
que pode funcionar.
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 fazer isso.
Existem alguns problemas, porém:
- Não é possível saber o caminho do executável sem executá-lo. os
type
,which
,command -v
… todos usam heurística para descobrir o caminho . Eles percorrem os componentes$PATH
e encontram o primeiro arquivo não pertencente ao diretório para o qual você tem permissão de execução. No entanto, depe encontrando no shell, quando se trata de executar o comando, muitos deles (Bourne, AT & T ksh, zsh, ash …) irão apenas executá-los na ordem de$PATH
até que aexecve
chamada do sistema não retorne com um erro. Por exemplo, se$PATH
contém/foo:/bar
e você deseja executarls
, eles tentarão primeiro para executar/foo/ls
ou se isso falhar/bar/ls
. Agora a 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
relataria/foo/ls
se você tivesse permissão de execução para/foo/ls
, mas executandols
pode realmente executar/bar/ls
se/foo/ls
não for um executável válido. - se
foo
for um interno, função ou alias,command -v foo
retornaráfoo
. Com alguns shells comoash
,pdksh
ouzsh
, também pode retornarfoo
se$PATH
incluir a string vazia e houver um arquivofoo
executável no diretório atual. Existem algumas circunstâncias em que você pode precisar levar isso em consideração. Lembre-se, por exemplo, que a lista de embutidos varia com a implementação do shell (por exemplo,mount
às vezes é embutido para busyboxsh
) e, por exemplo,bash
pode obter funções do ambiente. - if
$PATH
contém componentes do caminho relativo (normalmente.
ou a string 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 em que executa não será mais válido depois decd
em outro lugar. - Anedotal: com o shell ksh93, se
/opt/ast/bin
(embora esse caminho exato possa variar em diferentes sistemas, eu acredito) está em você$PATH
, ksh93 irá disponibilizar alguns builtins extras (chmod
,cmp
,cat
…), mascommand -v chmod
retornará/opt/ast/bin/chmod
mesmo se esse caminho não existir.
Determinando se um comando existe
Para descobrir se um determinado comando existe normalmente, você pode fazer:
if command -v given-command > /dev/null 2>&1; then echo given-command is available else echo given-command is not available fi
Onde se pode querer usar which
(t)csh
Em csh
e tcsh
, você não tem muita escolha. Em tcsh
, que “Tudo bem, pois which
está integrado. Em csh
, esse será o comando which
do sistema, que pode não fazer o que você deseja em alguns casos.
Encontre comandos apenas em alguns shells
Um caso em que pode fazer sentido usar which
é se você quiser saber o caminho de um comando, ignorando o potencial shell builtins ou funções em bash
, csh
(não tcsh
), dash
ou Bourne
scripts de shell, ou seja, shells que não têm whence -p
(como ksh
ou zsh
), command -ev
(como yash
), whatis -p
(rc
, akanga
) ou um (como tcsh
ou zsh
) em sistemas onde which
é disponível e não é o csh
script.
Se essas condições forem atendidas, então:
echo=$(which echo)
fornecerá o caminho do primeiro echo
em $PATH
(exceto em casos de canto), independentemente de echo
também ser um shell builtin / alias / function ou não.
Em outros shells, você “d prefere:
- zsh :
echo==echo
ouecho=$commands[echo]
ouecho=${${:-echo}:c}
- ksh , zsh :
echo=$(whence -p echo)
- yash :
echo=$(command -ev echo)
- rc , akanga :
echo=`whatis -p echo`
(cuidado com caminhos com espaços) - peixe :
set echo (type -fp echo)
Observe que se tudo o que você deseja fazer é executar aquele echo
comando, você não precisa obter seu caminho, você pode apenas fazer:
env echo this is not echoed by the builtin echo
Por exemplo, com tcsh
, para evitar que o which
interno 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 um comando externo. POSIX requer que todos os comandos internos do shell (como command
) também estejam disponíveis como comandos externos, mas infelizmente, esse “não é o caso de command
em muitos sistemas. Por exemplo, “é raro encontrar um comando command
em sistemas operacionais baseados em Linux, enquanto a maioria deles tem um which
(embora diferentes com opções e comportamentos diferentes).
Casos em que você pode querer um comando externo seriam onde quer que você executasse um comando sem invocar um shell POSIX.
O system("some command line")
, popen()
… funções de C ou várias linguagens invocam um shell para analisar essa linha de comando, então system("command -v my-cmd")
trabalhe neles. Uma exceção a isso seria perl
, que otimiza o shell se não ver nenhum caractere especial do shell (exceto espaço). Isso também se aplica ao seu operador crase:
$ 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 de :;
acima força perl
a invocar um shell ali. Usando which
, você não teria que usar esse truque.
Comentários
- @Joe,
which
é umcsh
script em muitos Unices comerciais. O motivo é histórico, que ‘ é por que eu contei a história, para que as pessoas entendam de onde veio, por que as pessoas se acostumaram a usá-la e por que realmente existe ‘ s nenhuma razão para você usá-lo. E sim, algumas pessoas usam (t) csh. Nem todo mundo usa Linux ainda - Depois de ler este post, descobri muito contexto para a resposta, mas não a resposta em si.Onde nesta postagem realmente diz por que não usar
which
, ao contrário de coisas que você pode estar tentando usarwhich
a fazer, a história dewhich
, implementações dewhich
, outros comandos para fazer tarefas relacionadas ou razões para realmente usarwhich
? Por que os outros comandos são melhores ? O que eles fazem de diferente dewhich
? Como eles evitam suas armadilhas? Esta resposta gasta mais palavras com os problemas com as alternativas do que com os problemas comwhich
. -
command
é descrito por POSIX. - @St é phaneChazelas Se eu criar um novo arquivo por
touch /usr/bin/mytestfile
e depois executarcommand -v mytestfile
, ele fornecerá o caminho (enquantowhich mytestfile
não). - @jarno, oh sim, você ‘ está certo.
bash
se estabelecerá em um arquivo não executável se não puder ‘ encontrar um executável, então ‘ s ” OK ” (embora na prática se prefiracommand -v
/type
retorna um erro) já que ‘ s o comando que tentaria executar quando você executassemytestfile
, mas odash
comportamento é problemático, como se houvesse ‘ um não executávelcmd
antes de um executável,command -v
retorna o não executável enquantocmd
executaria o executável (o errado um também é hash). FreeBSDsh
(também baseado emash
) tem o mesmo bug. zsh, yash, ksh, mksh, bash como sh estão OK.
Resposta
Os motivos pelos quais alguém pode não deseja usar which
já foi explicado, mas aqui estão alguns exemplos em alguns sistemas onde which
realmente falha.
Em shells tipo Bourne, estamos comparando a saída de which
com a saída de type
(type
sendo um shell embutido, é “a verdade fundamental, já que é o shell nos dizendo como chamaria um comando).
Muitos casos são casos extremos , mas lembre-se de que which
/ type
são frequentemente usados em casos extremos (para encontrar a resposta a um comportamento inesperado como: por que diabos esse comando está se comportando assim, qual deles estou chamando? ).
A maioria dos sistemas, a maioria dos shells tipo Bourne: funções
O caso mais óbvio é para funções:
$ type ls ls is a function ls () { [ -t 1 ] && set -- -F "$@"; command ls "$@" } $ which ls /bin/ls
A razão é que which
relata apenas sobre executáveis e, às vezes, sobre aliases (embora nem sempre os de seu shell), não funções.
O GNU cuja página de manual tem um exemplo corrompido (pois eles se esqueceram de citar $@
) sobre como usá-lo para relatar funções também, mas assim como para apelidos, porque não implementa um analisador de sintaxe de shell, é facilmente enganado:
$ which() { (alias; declare -f) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot "$@";} $ f() { echo $"\n}\ng ()\n{ echo bar;\n}\n" >> ~/foo; } $ type f f is a function f () { echo " } g () { echo bar; } " >> ~/foo } $ type g bash: type: g: not found $ which f f () { echo " } $ which g g () { echo bar; }
A maioria dos sistemas, a maioria dos shells tipo Bourne: builtins
Outro caso óbvio são os builtins ou palavras-chave, já que which
sendo um comando externo não há como saber quais são os builds do seu shell (e alguns shells como zsh
, bash
ou ksh
pode carregar builtins dinamicamente):
$ type echo . time echo is a shell builtin . is a shell builtin time is a shell keyword $ which echo . time /bin/echo which: no . in (/bin:/usr/bin) /usr/bin/time
(isso não se aplica a zsh
onde which
está integrado)
Solaris 10, AIX 7.1, HP / UX 11i, Tru64 5. 1 e muitos outros:
$ csh % which ls ls: aliased to ls -F % unalias ls % which ls ls: aliased to ls -F % ksh $ which ls ls: aliased to ls -F $ type ls ls is a tracked alias for /usr/bin/ls
Isso ocorre porque na maioria dos Unices comerciais, which
(como na implementação original no 3BSD) é um csh
script que lê ~/.cshrc
. Os aliases que ele relatará são aqueles definidos lá, independentemente dos aliases que você definiu atualmente e independentemente do shell que você está realmente usando.
Em HP / UX ou Tru64:
% echo "setenv PATH /bin:/usr/bin" >> ~/.cshrc % setenv PATH ~/bin:/bin:/usr/bin % ln -s /bin/ls ~/bin/ % which ls /bin/ls
(as versões Solaris e AIX corrigiram esse problema salvando $path
antes de ler ~/.cshrc
e restaurá-lo antes de pesquisar os comandos)
$ type "a b" a b is /home/stephane/bin/a b $ which "a b" no a in /usr/sbin /usr/bin no b in /usr/sbin /usr/bin
Ou:
$ d="$HOME/my bin" $ mkdir "$d"; PATH=$PATH:$d $ ln -s /bin/ls "$d/myls" $ type myls myls is /home/stephane/my bin/myls $ which myls no myls in /usr/sbin /usr/bin /home/stephane/my bin
(é claro, sendo um script csh
você não pode esperar que funcione com argumentos contendo espaços …)
CentOS 6.4, bash
$ type which which is aliased to `alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde" $ alias foo=": "|test|"" $ which foo alias foo=": "|test|"" /usr/bin/test $ alias $"foo=\nalias bar=" $ unalias bar -bash: unalias: bar: not found $ which bar alias bar="
Nesse sistema, há um alias definido em todo o sistema que envolve o comando GNU which
.
A saída falsa é porque which
lê a saída de bash
“s alias
mas não sabe como analisá-lo adequadamente e usa heurística (um alias por linha, procura o primeiro comando encontrado após |
, ;
, &
…)
A pior coisa no CentOS é que zsh
tem um comando interno which
perfeitamente bom, mas o CentOS conseguiu quebrá-lo substituindo-o por um alias não funcional para GNU which
.
Debian 7.0, ksh93:
(embora se aplique à maioria dos sistemas com muitos shells)
$ unset PATH $ which which /usr/local/bin/which $ type which which is a tracked alias for /bin/which
No Debian, /bin/which
é um script /bin/sh
. No meu caso, sh
sendo dash
, mas é o mesmo quando “s bash
.
Uma PATH
não definida é para não desativar PATH
pesquisa, mas significa usar o sistema “s PATH padrão que, infelizmente, no Debian, ninguém concorda (dash
e bash
têm /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
, zsh
tem /bin:/usr/bin:/usr/ucb:/usr/local/bin
, ksh93
tem /bin:/usr/bin
, mksh
tem /usr/bin:/bin
($(getconf PATH)
), execvp()
(como em env
) tem :/bin:/usr/bin
(sim, procure no diretório atual primeiro!)) .
É por isso que which
errou acima, já que está usando o dash
“padrão PATH
que é diferente de ksh93
“s
É não t melhor com GNU which
que relata:
which: no which in ((null))
(curiosamente, existe de fato um /usr/local/bin/which
em meu sistema, que na verdade é um akanga
script que veio com akanga
(um rc
derivado de shell em que o PATH
padrão é /usr/ucb:/usr/bin:/bin:.
))
bash, qualquer sistema:
Aquele que Chris está se referindo em sua resposta :
$ PATH=$HOME/bin:/bin $ ls /dev/null /dev/null $ cp /bin/ls bin $ type ls ls is hashed (/bin/ls) $ command -v ls /bin/ls $ which ls /home/chazelas/bin/ls
Também após chamar hash
manualmente:
$ type -a which which is /usr/local/bin/which which is /usr/bin/which which is /bin/which $ hash -p /bin/which which $ which which /usr/local/bin/which $ type which which is hashed (/bin/which)
Agora, um caso em que which
e às vezes type
falham:
$ mkdir a b $ echo "#!/bin/echo" > a/foo $ echo "#!/" > b/foo $ chmod +x a/foo b/foo $ PATH=b:a:$PATH $ which foo b/foo $ type foo foo is b/foo
Agora, com alguns shells:
$ foo bash: ./b/foo: /: bad interpreter: Permission denied
Com outros:
$ foo a/foo
Nem which
nem type
pode saber com antecedência que b/foo
não pode b e executado. Alguns shells como bash
, ksh
ou yash
, ao invocar foo
realmente tentará executar b/foo
e relatar um erro, enquanto outros (como zsh
, ash
, csh
, Bourne
, tcsh
) será executado a/foo
após a falha da execve()
chamada do sistema em b/foo
.
Comentários
-
mksh
realmente usa algo diferente para o padrão$PATH
: primeiro , a constante de tempo de compilação do sistema operacional_PATH_DEFPATH
é usada (mais comumente nos BSDs), então,confstr(_CS_PATH, …)
é usado (POSIX), e se ambos não existirem ou falharem,/bin:/usr/bin:/sbin:/usr/sbin
é usado. - Em seu primeiro exemplo, mesmo se
ls
é uma função que está usando gls
de PATH. Ewhich
é bom para dizer qual é usado/usr/bin/ls
ou/usr/local/bin/ls
. Eu não ‘ t ver ” Por que não usar qual ” …. - @rudimeier, Isso
which ls
me dará/bin/ls
independentemente de ols
chamadas de função/bin/ls
ou/opt/gnu/bin/ls
oudir
ou nada. IOW,which
(que implementações, IMMV) está dando algo irrelevante - @St é phaneChazelas. Não não não. Eu sei já que meu
ls
é uma função. Eu sei que minha funçãols
está chamandols
dePATH
. Agorawhich
me diz onde está o arquivo. Você vê apenas um único caso de uso: ” O que meu shell faria com este comando. ” Para este caso de usowhich
está errado, correto.Mas há outros casos de uso em que (GNU)which
é exatamente a coisa certa. - @rudimeter, depende do
which
implementação. Alguns dirão que ‘ é um alias (se você tiver um alias configurado ou se houver um~/.cshrc
em sua casa que tenha tal alias), alguns fornecerão um caminho, mas o caminho errado em algumas condições.sh -c 'command -v ls'
, embora não seja perfeito, tem mais probabilidade de dar a resposta certa para esse requisito diferente (e também é padrão).
Resposta
Uma coisa que (pela minha rápida leitura) parece que Stephane não mencionou é que which
tem nenhuma ideia sobre a tabela de hash de caminho do shell. Isso tem o efeito de retornar um resultado que não é representativo do que realmente é executado, o que o torna ineficaz na depuração.
Resposta
Eu geralmente me encolho quando essa pergunta é recomendada para usuários desavisados porque criticar which
não é útil para ninguém.
Se which
funciona bem e fornece a resposta correta para algumas tarefas, seguindo a moto do Unix: faça uma coisa, faça bem , por que deveria which
ser banido?
A questão deveria ser, então, qual funciona bem e faz bem um trabalho específico?
Por exemplo, o utilitário externo em / bin / que no Debian é um script de shell cujo objetivo é apenas listar executáveis com o nome fornecido no caminho. Acredito que which
cumpre o objetivo pretendido corretamente. Ele não carrega apelidos, funções, nada do shell, apenas lista os primeiros (ou todos) executáveis do nome fornecido no PATH. O que significa de ter encontrado um arquivo com o mesmo nome fornecido é algo que o usuário deve descobrir por ela (ele) self.
Sim, outras which
implementações podem (e geralmente têm) problemas específicos.
Resposta
Frequentemente ouvimos coisas que devem ser evitadas. Porque? O que devemos usar no lugar?
Nunca ouvi isso. Forneça exemplos específicos. Eu me preocuparia com sua distribuição Linux e pacotes de software instalados, pois é daí que vem which
!
SLES 11.4 x86-64
na versão tcsh 6.18.01:
> which which which: shell built-in command.
na versão do bash 3.2-147:
> which which /usr/bin/which > which -v GNU which v2.19, Copyright (C) 1999 - 2008 Carlo Wood. GNU which comes with ABSOLUTELY NO WARRANTY; This program is free software; your freedom to use, change and distribute this program is protected by the GPL.
which
faz parte de util-linux um pacote padrão distribuído pela Organização do Kernel Linux para uso como parte do sistema operacional Linux. Ele também fornece esses outros arquivos
/bin/dmesg /bin/findmnt /bin/logger /bin/lsblk /bin/more /bin/mount /bin/umount /sbin/adjtimex /sbin/agetty /sbin/blkid /sbin/blockdev /sbin/cfdisk /sbin/chcpu /sbin/ctrlaltdel /sbin/elvtune /sbin/fdisk /sbin/findfs /sbin/fsck /sbin/fsck.cramfs /sbin/fsck.minix /sbin/fsfreeze /sbin/fstrim /sbin/hwclock /sbin/losetup /sbin/mkfs /sbin/mkfs.bfs /sbin/mkfs.cramfs /sbin/mkfs.minix /sbin/mkswap /sbin/nologin /sbin/pivot_root /sbin/raw /sbin/sfdisk /sbin/swaplabel /sbin/swapoff /sbin/swapon /sbin/switch_root /sbin/wipefs /usr/bin/cal /usr/bin/chrp-addnote /usr/bin/chrt /usr/bin/col /usr/bin/colcrt /usr/bin/colrm /usr/bin/column /usr/bin/cytune /usr/bin/ddate /usr/bin/fallocate /usr/bin/flock /usr/bin/getopt /usr/bin/hexdump /usr/bin/i386 /usr/bin/ionice /usr/bin/ipcmk /usr/bin/ipcrm /usr/bin/ipcs /usr/bin/isosize /usr/bin/line /usr/bin/linux32 /usr/bin/linux64 /usr/bin/look /usr/bin/lscpu /usr/bin/mcookie /usr/bin/mesg /usr/bin/mkzimage_cmdline /usr/bin/namei /usr/bin/rename /usr/bin/renice /usr/bin/rev /usr/bin/script /usr/bin/scriptreplay /usr/bin/setarch /usr/bin/setsid /usr/bin/setterm /usr/bin/tailf /usr/bin/taskset /usr/bin/time /usr/bin/ul /usr/bin/uname26 /usr/bin/unshare /usr/bin/uuidgen /usr/bin/wall /usr/bin/whereis /usr/bin/which /usr/bin/write /usr/bin/x86_64 /usr/sbin/addpart /usr/sbin/delpart /usr/sbin/fdformat /usr/sbin/flushb /usr/sbin/freeramdisk /usr/sbin/klogconsole /usr/sbin/ldattach /usr/sbin/partx /usr/sbin/rcraw /usr/sbin/readprofile /usr/sbin/rtcwake /usr/sbin/setctsid /usr/sbin/tunelp
my util-linux
é a versão 2.19. As notas de versão podem ser facilmente encontradas na v2.13 datada de (28 de agosto de 2007). Não tenho certeza de qual era o ponto ou objetivo disso, certamente não foi respondido naquela coisa longa com votos positivos 331 vezes.
Comentários
- Observe como o questão não faz nenhuma menção de a que Unix se refere. Linux é apenas um de alguns.
- Como seu
which -v
mostra, aquele ‘ é GNU que (o extravagante um mencionado na outra resposta e não é de forma alguma específico para Linux), não util-linux, que a AFAIK nunca incluiu um utilitáriowhich
. util-linux 2.19 é de 2011, GNU que 2.19 é de 2008.
which
pressupõe um contexto de shell interativo. Esta questão é marcada / portabilidade. Então, eu interpreto a questão neste contexto como ” o que usar em vez dewhich
para encontrar o primeiro executável de um determinado nome no$PATH
“. A maioria das respostas e razões contrawhich
lidam com aliases, builtins e funções que, na maioria dos shell scripts portáteis do mundo real, são apenas de interesse acadêmico. Aliases definidos localmente não são ‘ t herdados ao executar um script de shell (a menos que você o fonte com.
).csh
(ewhich
ainda é umcsh
script na maioria dos comerciais Unices) lê~/.cshrc
quando não interativo. É ‘ por isso que você ‘ notará que os scripts csh geralmente começam com#! /bin/csh -f
.which
não porque tem o objetivo de fornecer os aliases, porque ‘ é uma ferramenta para usuários (interativos) decsh
. Os usuários de shells POSIX têmcommand -v
.(t)csh
(ou você ‘ não se importa se não ‘ fornecer o resultado correto), usetype
oucommand -v
em vez disso . Veja as respostas para por quê .stat $(which ls)
está errado por vários motivos (falta--
, aspas ausentes), não apenas o uso dewhich
). Você ‘ d usastat -- "$(command -v ls)"
. Isso assume quels
é de fato um comando encontrado no sistema de arquivos (não um embutido em seu shell ou função de alias).which
pode fornecer o caminho errado (não o caminho que seu shell executaria se você inserissels
) ou fornecer um alias conforme definido na configuração de alguns outros shells …which
implementações não forneceriam nem mesmo ols
que seria encontrado por uma pesquisa de$PATH
(independentemente do quels
pode invocar em seu shell).sh -c 'command -v ls'
ouzsh -c 'rpm -q --whatprovides =ls'
são mais propensos a fornecer a resposta correta. A questão aqui é quewhich
é uma herança quebrada decsh
.