我想使用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 特有的方法。以下是一些可能有效的想法:
过滤掉用户主目录(和相对路径)中的版本:
( 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 )
过滤掉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 )
过滤掉可写的红宝石(最后的手段,假设不以 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 )
询问
rvm
,chruby
等等。( 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 中,您应该使用type
或command
命令。请参阅此问答以了解原因,标题为:为什么不用“哪个”呢?那该用什么呢?。
例子
$ 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"