Como eu uso qual (1) com o $ PATH padrão do sistema?

5

Eu gostaria de usar which com o caminho padrão do sistema, ignorando quaisquer enfeites dos arquivos de configuração do shell do usuário.

Motivação

Eu estou tentando escrever um script para encontrar o binário Ruby do sistema. Muitos desenvolvedores Ruby usam um gerenciador de versão Ruby, que adiciona algo como ~/.rvm/bin ao início de seu $PATH . Eu quero ignorar isso e usar a versão do Ruby que veio com o sistema, ou foi instalado através do gerenciador de pacotes do sistema.

Solução atual

Veja o que tentei até agora:

$ env -i sh -c "which ruby"

Isso não gera saída e sai com 1. Espero que funcione, porque o caminho inclui /usr/bin e meu sistema veio com um binário Ruby em /usr/bin/ruby :

$ env -i sh -c "echo \$PATH"
/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.
$ which -a ruby
# ...
/usr/bin/ruby

Alguns detalhes adicionais:

  • env -s bash -c "which ruby" também não encontra nada.
  • env -i zsh -c "which ruby" encontra /usr/bin/ruby , mas não posso depender de zsh .
  • Usar o caminho completo para which (para ter certeza de que estou usando o binário, não o shell integrado) não faz nenhuma diferença.

Meu ambiente

Estou escrevendo isso no Bash no OS X, mas gostaria que ele fosse portável para outros shells e sistemas operacionais.

    
por georgebrock 23.11.2013 / 12:22

4 respostas

5

command -pv usa um "valor padrão para PATH".

$ which ruby
/home/mikel/.rvm/rubies/ruby-1.9.3-p484/bin/ruby

$ command -pv ruby
/usr/bin/ruby

Infelizmente, isso não funciona em zsh , então, com base no comentário de Stephane, poderíamos usar getconf PATH :

$ PATH=$(getconf PATH) which ruby

ou use command -v no lugar de which , conforme recomendado em Por que não usar" que "? O que usar então?

$ PATH=$(getconf PATH) command -v ruby

A desvantagem dessas abordagens é que, se o administrador do sistema instalasse uma versão do sistema em /usr/local/bin ou /opt/local/bin , e todos os usuários tivessem isso em PATH (por exemplo, via /etc/profile ou /etc/environment ou semelhante), o acima provavelmente não iria encontrá-lo.

No seu caso específico, sugiro tentar algo específico para Ruby. Veja algumas ideias que podem funcionar:

  1. Filtrar versões no diretório pessoal do usuário (e caminhos relativos):

    (
        IFS=:
        set -f
        for dir in $PATH; do
            case $dir/ in
                "$HOME/"*) ;;
                /*/)
                    if [ -f "$dir/ruby" ] && [ -x "$dir/ruby" ]; then
                        printf '%s\n' "$dir/ruby"
                        break
                    fi;;
            esac
        done
    )
    
  2. Filtre as versões no diretório rvm

    (
        IFS=:
        set -f
        for dir in $PATH; do
            case $dir/ in
                "$rvmpath/"*) ;;
                /*/)
                    if [ -f "$dir/ruby" ] && [ -x "$dir/ruby" ]; then
                        printf '%s\n' "$dir/ruby"
                        break
                    fi;;
            esac
        done
    )
    
  3. Filtre rubis graváveis (último recurso, presume que não está sendo executado como root)

    (
        IFS=:
        set -f
        for dir in $PATH; do
            case $dir/ in
                /*/)
                    ruby=$dir/ruby
                    if [ -f "$ruby" ] && [ -x "$ruby" ] && [ ! -w "$ruby" ]; then
                        printf '%s\n' "$ruby"
                        break
                    fi;;
            esac
        done
    )
    
  4. Peça rvm , chruby , etc.

    (
        rvm system
        chruby system
        command -v ruby
    )
    

A última maneira faz com que rvm selecione o Ruby padrão, mas o fazemos em um subshell, para que o Ruby preferido do usuário seja restaurado posteriormente. ( chruby parte não testada.)

    
por 23.11.2013 / 20:55
3

Definindo explicitamente o valor de $PATH no sub-shell resolve o problema:

env -i sh -c "PATH=\$PATH which ruby"

Observe que $ em $PATH está com escape, o que significa que $PATH no comando sub-shell não é substituído pelo valor do shell pai de $PATH antes de o comando ser executado (isso também pode ser alcançado usando aspas simples).

Eu estaria realmente interessado em saber por que isso é necessário neste caso.

    
por 23.11.2013 / 12:59
3

Você geralmente não quer usar o comando which . No Bash, você deve usar os comandos type ou command . Veja este Q & A por razões, intitulado: Por que não usar "qual"? O que usar então? .

Exemplos

$ type -a ls
ls is aliased to 'ls --color=auto'
ls is /bin/ls

ou isto:

$ type -a vim
vim is /usr/bin/vim

ou isto:

$ command -v ls
alias ls='ls --color=auto'

ou isto:

$ command -v vim
/usr/bin/vim

Da página man do Bash.

trecho no tipo

type [-aftpP] name [name ...]
     With no options, indicate how each name would be interpreted if used as a
     command name.  If the -t option is used, type  prints a  string  which  is
     one  of  alias, keyword, function, builtin, or file if name is an alias,
     shell reserved word, function, builtin, or disk file, respectively.  If the
     name is not found, then nothing is  printed, and  an exit status of false
     is returned.  If the -p option is used, type either returns the name of the
     disk file that would be executed if name were specified as a command name,
     or nothing if ''type -t name'' would  not  return file.   The  -P  option
     forces a PATH search for each name, even if ''type -t name'' would not
     return file.  If a command is hashed, -p and -P print the hashed value, not
     necessarily the file that appears first in PATH.  If  the -a option is used,
     type prints all of the places that contain an executable named name.  This
     includes aliases and functions, if and only if the -p option is not also
     used.  The table of hashed commands  is  not  consulted  when using  -a.
     The -f option suppresses shell function lookup, as with the command
     builtin.  type returns true if all of the arguments are found, false if
     any are not found.

trecho no comando

   command [-pVv] command [arg ...]
         Run command with args suppressing the normal shell function lookup.
         Only builtin commands or commands found in the PATH are executed.  If
         the -p option is given, the search for command is performed using a
         default value for PATH that  is  guaranteed to find all of the standard
         utilities.  If either the -V or -v option is supplied, a description of
         command is printed.  The -v option causes a single word indicating the
         command or file name used to invoke command to  be displayed; the -V
         option produces a more verbose description.  If the -V or -v option is
         supplied, the exit status is 0 if command was found, and 1 if not.
         If neither option is supplied and an error  occurred  or command
         cannot be  found, the exit status is 127.  Otherwise, the exit status
         of the command builtin is the exit status of command.
    
por 23.11.2013 / 14:44
1

Especifique a configuração PATH de simplesmente "/ usr / bin" (ou configure PATH para se adequar) como um parâmetro para env. env -i ignora o ambiente que herda, a seguinte configuração PATH permitirá que você encontre / usr / bin / ruby.

env -i PATH="/usr/bin" sh -c "which ruby" 
    
por 23.11.2013 / 12:34