Por que não usar “ que ”? O que usar então?

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

  • Acho que a maioria dos argumentos contra o uso de 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 de which para encontrar o primeiro executável de um determinado nome no $PATH “. A maioria das respostas e razões contra which 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 .).
  • @MattBianco, sim, csh (e which ainda é um csh 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) de csh. Os usuários de shells POSIX têm command -v.
  • @rudimeier, então a resposta seria sempre, a menos que seu shell seja (t)csh (ou você ‘ não se importa se não ‘ fornecer o resultado correto), use type ou command -v em vez disso . Veja as respostas para por quê .
  • @rudimeier, (stat $(which ls) está errado por vários motivos (falta --, aspas ausentes), não apenas o uso de which). Você ‘ d usa stat -- "$(command -v ls)". Isso assume que ls é 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ê inserisse ls) ou fornecer um alias conforme definido na configuração de alguns outros shells …
  • @rudimeier, novamente, há uma série de condições sob as quais muitas which implementações não forneceriam nem mesmo o ls que seria encontrado por uma pesquisa de $PATH (independentemente do que ls pode invocar em seu shell). sh -c 'command -v ls' ou zsh -c 'rpm -q --whatprovides =ls' são mais propensos a fornecer a resposta correta. A questão aqui é que which é uma herança quebrada de csh.

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 a execve chamada do sistema não retorne com um erro. Por exemplo, se $PATH contém /foo:/bar e você deseja executar ls, 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 executando ls 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 como ash, pdksh ou zsh, também pode retornar foo se $PATH incluir a string vazia e houver um arquivo foo 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 busybox sh) 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 de cd 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 …), mas command -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 ou echo=$commands[echo] ou echo=${${:-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 é um csh 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 usar which a fazer, a história de which, implementações de which, outros comandos para fazer tarefas relacionadas ou razões para realmente usar which? Por que os outros comandos são melhores ? O que eles fazem de diferente de which? Como eles evitam suas armadilhas? Esta resposta gasta mais palavras com os problemas com as alternativas do que com os problemas com which.
  • command é descrito por POSIX.
  • @St é phaneChazelas Se eu criar um novo arquivo por touch /usr/bin/mytestfile e depois executar command -v mytestfile, ele fornecerá o caminho (enquanto which 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 prefira command -v / type retorna um erro) já que ‘ s o comando que tentaria executar quando você executasse mytestfile, mas o dash comportamento é problemático, como se houvesse ‘ um não executável cmd antes de um executável, command -v retorna o não executável enquanto cmd executaria o executável (o errado um também é hash). FreeBSD sh (também baseado em ash) 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 g ls de PATH. E which é 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 o ls chamadas de função /bin/ls ou /opt/gnu/bin/ls ou dir 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ção ls está chamando ls de PATH. Agora which 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 uso which 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ário which. util-linux 2.19 é de 2011, GNU que 2.19 é de 2008.

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *