如何将 which(1) 与系统默认的 $PATH 一起使用?

如何将 which(1) 与系统默认的 $PATH 一起使用?

我想使用which系统的默认路径,忽略用户 shell 配置文件中的任何修饰。

动机

我正在尝试编写一个脚本来查找系统的 Ruby 二进制文件。许多 Ruby 开发人员使用 Ruby 版本管理器,它会~/.rvm/bin在他们的$PATH.我想绕过这个并使用系统附带的Ruby版本,或者通过系统的包管理器安装的Ruby版本。

目前的解决方案

到目前为止,这是我尝试过的:

$ env -i sh -c "which ruby"

这不会给出任何输出,并以 1 退出。不过我希望它能够工作,因为路径包含/usr/bin,并且我的系统附带了一个 Ruby 二进制文件,位置为/usr/bin/ruby

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

一些额外的细节:

  • env -s bash -c "which ruby"也没有发现任何东西。
  • env -i zsh -c "which ruby"确实找到了/usr/bin/ruby,但我不能依赖zsh
  • 使用完整路径which(以确保我使用的是二进制文件,而不是内置的 shell)没有任何区别。

我的环境

我在 OS X 上的 Bash 中编写此内容,但希望它能够移植到其他 shell 和操作系统。

答案1

command -pv使用“PATH 的默认值”。

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

$ command -pv ruby
/usr/bin/ruby

不幸的是,这在 中不起作用zsh,因此根据 Stephane 的评论,我们可以使用getconf PATH

$ PATH=$(getconf PATH) which ruby

command -v按照中的which建议使用为什么不用“哪个”呢?那该用什么呢?

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

这些方法的缺点是,如果系统管理员将系统范围的版本安装到例如/usr/local/bin或中/opt/local/bin,并且所有用户都拥有该版本PATH(例如通过/etc/profile/etc/environment类似),则上述方法可能找不到它。

根据您的具体情况,我建议尝试 Ruby 特有的方法。以下是一些可能有效的想法:

  1. 过滤掉用户主目录(和相对路径)中的版本:

    (
        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. 过滤掉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. 过滤掉可写的红宝石(最后的手段,假设不以 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. 询问rvmchruby等等。

    (
        rvm system
        chruby system
        command -v ruby
    )
    

最后一种方法rvm选择默认的 Ruby,但我们在子 shell 中执行此操作,以便随后恢复用户首选的 Ruby。 (chruby部分未经测试。)

答案2

$PATH在子 shell 中显式设置 的值可以解决该问题:

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

请注意,$in$PATH被转义,这意味着在执行命令之前,$PATH子 shell 命令不会被父 shell 的值替换(这也可以使用单引号来实现)。$PATH

我真的很想知道为什么在这种情况下这是必要的。

答案3

您通常不想使用该which命令。在 Bash 中,您应该使用typecommand命令。请参阅此问答以了解原因,标题为:为什么不用“哪个”呢?那该用什么呢?

例子

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

或这个:

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

或这个:

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

或这个:

$ command -v vim
/usr/bin/vim

来自 Bash 的手册页。

类型摘录

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.

根据命令摘录

   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.

答案4

将 PATH 设置简单地指定为“/usr/bin”(或设置适合的 PATH)作为 env 的参数。 env -i 忽略它继承的环境,以下 PATH 设置将启用它来查找 /usr/bin/ruby。

env -i PATH="/usr/bin" sh -c "which ruby" 

相关内容