当寻找可执行文件的路径或检查如果在Unix shell中输入命令名会发生什么情况时,有很多不同的实用程序(whichtypecommandwhencewherewhereiswhatishash等)。

我们经常听到应该避免which。为什么?我们应该改用什么呢?

评论

我认为大多数反对使用哪种说法都假设是交互式外壳上下文。这个问题被标记为/可移植性。因此,我将这种情况下的问题解释为“使用什么代替在$ PATH中找到给定名称的第一个可执行文件”。处理别名,内建函数和函数的大多数答案和原因,在大多数现实世界中的可移植shell脚本中,它们只是具有学术意义。运行shell脚本时,本地定义的别名不会被继承(除非您使用。来获取它。)。

@MattBianco,是的,csh(并且在大多数商业Unices上仍然是csh脚本)在非交互式时会读取〜/ .cshrc。这就是为什么您会注意到csh脚本通常以#开头的原因! / bin / csh -f。这样做并不是因为它旨在为您提供别名,而是因为它是csh(交互式)用户的工具。 POSIX shell用户具有命令-v。

@rudimeier,那么除非您的shell是(t)csh(或者您不介意它没有给您正确的结果),否则答案总是总是这样,请改用type或-v命令。查看原因的答案。

@rudimeier,(stat $(哪个ls)是错误的,原因有几个(缺少-,缺少引号),而不仅仅是用法。您将使用stat-“ $(command -v ls)”。假定ls确实是在文件系统上找到的命令(而不是外壳程序的内置命令或别名功能)。这可能会给您错误的路径(而不是您输入ls时shell将执行的路径)或给您提供其他一些shell的配置中定义的别名...

再次,@ rudimeier,在许多情况下,许多实现都不会给您甚至是通过查找$ PATH所找到的ls(无论ls在您的shell中调用什么)。 sh -c'命令-v ls'或zsh -c'rpm -q --whatprovides = ls'更可能为您提供正确的答案。这里的重点是csh的继承遗产。

#1 楼

这是您从未想过的所有信息:

摘要

在类似Bourne的shell脚本中获取可执行文件的路径名(有一些警告;请参见下文):

ls=$(command -v ls)


找出给定命令是否存在:

if command -v given-command > /dev/null 2>&1; then
  echo given-command is available
else
  echo given-command is not available
fi


在类似Bourne的交互式shell的提示下: shells。

用例

在脚本中查找该信息或在shell提示符下以交互方式查找信息。

在shell提示,典型的用例是:该命令的行为很奇怪,我使用的是正确的命令吗?键入which时究竟发生了什么?我可以进一步看一下它是什么吗?

在这种情况下,您想知道在调用命令而不实际调用命令时shell会执行什么操作。

在shell中脚本,它往往有很大的不同。在shell脚本中,没有理由要运行命令就知道命令在哪里或什么地方。通常,您想知道的是可执行文件的路径,因此您可以从中获取更多信息(例如相对于另一个文件的路径,或者从该路径中的可执行文件的内容中读取信息)。 br />
交互式地,您可能想了解系统中所有可用的脚本中的mycmd命令,很少见。

大多数可用工具(通常是这种情况)被设计为可以交互使用。

历史记录

历史悠久。

早期的Unix外壳直到70年代后期都没有功能或别名。仅传统上在my-cmd中查找可执行文件。 $PATH在1978年左右引入了别名(尽管1979年5月在csh中首次发布了csh),还处理了2BSD供用户自定义外壳程序(每个外壳程序,例如.cshrc,即使在脚本中不是交互性的,也读取csh)。

虽然Bourne shell于1979年初在Unix V7中首次发布,但功能支持才添加到很晚(在SVR2中为1984),无论如何,它从来没有任何.cshrc文件(rc用于配置)您的环境,而不是外壳本身)。

.profile比Bourne shell流行得多,因为(尽管它的语法比Bourne shell糟糕得多)它增加了很多方便

csh(1980)中,为3BSD用户添加了which csh脚本,以帮助识别可执行文件,在许多情况下,您都可以找到与csh几乎相同的脚本当今的商业Unices(例如Solaris,HP / UX,AIX或Tru64)。该脚本读取用户的which(就像所有~/.cshrc脚本一样,除非使用csh调用),然后在别名列表和csh -f(即$path根据csh维护的数组)。

您去了:$PATH是当时最受欢迎的外壳程序中的第一名(并且which直到90年代中期仍很流行),这是主要原因为什么将其记录在书中并仍被广泛使用。

请注意,即使对于csh用户,该csh csh脚本也不一定能为您提供正确的信息。它获取在which中定义的别名,而不是您稍后在提示符下定义的别名,或者例如通过~/.cshrc另一个source文件定义的别名,(尽管这不是一个好主意),但可以在csh中重新定义PATH。 />
从Bourne shell运行该~/.cshrc命令仍会查找您在which中定义的别名,但是如果您没有使用~/.cshrc而不是它,那仍然可能会找到正确的答案。<直到1984年,使用内置命令csh在SVR2中,Bourne shell才添加了类似的功能。它是内置的(而不是外部脚本),这意味着它可以(在某种程度上)为您提供正确的信息,因为它可以访问Shell的内部。

初始type该命令与type脚本存在类似的问题,因为如果未找到该命令,它不会返回故障退出状态。另外,对于可执行文件,与which相反,它输出的内容类似于which而不是ls is /bin/ls,这使得在脚本中使用起来不那么容易。内置的/bin/ls重命名为type。而Plan9(曾经是Unix的继任者)外壳whatis(及其派生的rcakanga)也具有es。基于),开发于80年代中期,但在1988年之前尚未广泛使用,在Bourne外壳顶部添加了许多whatis功能(行编辑器,别名...)。它添加了自己的内置sh(除了csh之外),它具有多个选项(whence提供类似type的详细输出,而-v仅查找可执行文件(不是别名/函数...))。

巧合的是,在AT&T与伯克利之间的版权问题上出现了动荡,一些免费的软件外壳实现在80年代末90年代初问世。所有的Almquist外壳(type,将替换BSD中的Bourne外壳),-pash),ksh(由FSF赞助),pdksh的公共领域实现在1989年至1991年之间问世。 >
Ash虽然是Bourne shell的替代品,但直到很晚(在NetBSD 1.3和FreeBSD 2.3中)才内置bash,尽管它具有zsh。 OSF / 1 type具有内置的hash -v,在OSF / 1 v3.x之前始终返回0。 /bin/sh并未添加type,而是向bash添加了whence选项以打印路径(-p类似于type),而type -p报告所有匹配的命令。 whence -p内置-a并添加了一个tcsh命令,其作用类似于whichwherebash拥有它们的全部。

type -a shell(2005)具有作为功能实现的zsh命令。

fish csh脚本同时已从NetBSD中删除(因为它是内置的)在tcsh中使用,并且在其他shell中使用不多),并且添加到type中的功能(当以which调用时,whereis的行为与which相似,只是它仅在whereis中查找可执行文件)。在OpenBSD和FreeBSD中,which也更改为用C编写的代码,仅在$PATH中查找命令。

实现

which命令的数十种实现在各种形式上具有不同语法和行为的Unices。

在Linux上(除了$PATHwhich中的内置对象),我们找到了几种实现。例如,在最近的Debian系统上,这是一个简单的POSIX Shell脚本,用于在tcsh中查找命令。

zsh也具有$PATH命令。

busybox which可能是最奢侈的了。它尝试将GNU csh脚本的功能扩展到其他shell:您可以告诉它别名和函数是什么,以便它可以为您提供更好的答案(我相信某些Linux发行版会围绕which设置一些全局别名

which有几个运算符可扩展到可执行文件的路径:bash文件名扩展运算符和zsh历史记录扩展修饰符(此处应用于参数扩展):

type ls


=,在:c模块中还使命令哈希表作为zsh关联数组:

$ print -r -- =ls
/bin/ls
$ cmd=ls; print -r -- $cmd:c
/bin/ls


zsh/parameters实用程序(除了Unix V8 Bourne shell或Plan 9中的那个之外,commands / whatis并不是真正相关的,因为它仅用于文档编制(使用whatis数据库,即手册页摘要')。尽管它是用q431207编写的,但也与rc同时在es中添加了9q而不是whereis,用于同时查找可执行文件,手册页和源,但不是基于当前环境。因此,再次回答了一个不同的需求。

现在,在标准方面,POSIX指定3BSDwhich命令(在POSIX.2008之前是可选的)。 UNIX指定C命令(无选项)。仅此而已(在任何标准中都未指定cshcommand -v-V)。

对于某些版本,在Linux Standard Base规范中typewhere是可选的,这解释了为什么例如某些旧版本的which (尽管基于同时具有两者的whence)都没有。 type也已添加到某些Bourne shell实现中(例如在Solaris上)。

Status Today

如今的状态是command -vposh在所有类似Bourne的shell中都是普遍存在的(不过,如@jarno所指出的,请注意,当不在POSIX模式下时,请注意pdksh中的警告/错误,或者在下面的注释中使用Almquist shell的某些后代)。 command -v是您唯一要使用type的外壳程序(因为那里没有command -v且内置了bash)。只要在我们的tcshwhich或任何Shell启动文件中没有相同名称的别名或函数,并且您未在type中定义which,则可执行文件是可执行的。如果您为它定义了别名或函数,它可能不会告诉您,或者告诉您错误的内容。

如果您想了解给定名称的所有命令,没有可移植的东西。您将在tcshzsh中使用which,在~/.cshrc~/.bashrc中使用$PATH,在ksh93中使用~/.cshrc,在其他外壳中,也可以将wheretcsh结合使用,这可能会起作用。

建议

获取可执行文件的路径名

现在,要获取脚本中可执行文件的路径名,需要注意以下几点:

$ print -r -- $commands[ls]
/bin/ls


将是执行此操作的标准方法。

尽管存在一些问题:


不执行可执行文件就无法知道其路径。所有zshtype -abash ...都使用启发式算法找出路径。它们遍历zsh组件,并找到您具有执行权限的第一个非目录文件。但是,根据外壳程序的不同,在执行命令时,它们中的许多命令(Bourne,AT&T ksh,zsh,ash ...)将按whence -a的顺序执行,直到系统调用type不返回时为止。一个错误。例如,如果which -a包含type,并且您要执行which,则他们将首先尝试执行command -v,否则将失败。现在执行$PATH可能会失败,因为您没有执行权限,但由于其他原因(例如它不是有效的可执行文件),也可能导致执行失败。如果您具有对$PATH的执行权限,则execve将报告$PATH,但如果/foo:/bar不是有效的可执行文件,则运行ls可能实际上会运行/foo/ls。对于某些外壳程序,例如/bar/ls/foo/lscommand -v ls,如果/foo/ls包含空字符串并且当前目录中存在可执行文件/foo/ls,它也可能返回ls。在某些情况下,您可能需要考虑到这一点。请记住,例如,内建的列表随shell实现的不同而变化(例如,/bar/ls有时内置于busybox /foo/ls),例如foo可以从环境中获取函数。
如果command -v foo包含相对路径组件(通常foo或空字符串都引用当前目录,但可以是任何东西),具体取决于外壳,ash可能不会输出绝对路径。因此,在其他地方运行pdksh时,您在运行zsh时获得的路径将不再有效。
轶事:如果使用ksh93外壳,则如果foo(尽管我相信确切的路径可能会因不同系统而异)在您的体内,则ksh93将提供一些额外的内置函数($PATHfoomount ...),但sh将返回bash即使该路径不存在。

确定命令是否存在

要确定给定命令是否标准存在,可以执行以下操作:

ls=$(command -v ls)


可能要使用$PATH的地方


.

command -v cmdcommand -v中,您没有很多选择。在cd中,内置/opt/ast/bin很好。在$PATH中,这将是系统chmod命令,在某些情况下可能无法满足您的要求。

仅在某些外壳中查找命令

使用cmp很有意义,如果您想知道命令的路径,而忽略catcommand -v chmod(不是/opt/ast/bin/chmod),which(t)csh Shell脚本(即没有csh(如tcshtcsh),which(例如csh),whichwhichbash)或内置csh(例如tcshdash)在Bourne可用且不是whence -p脚本的系统上。
满足条件,然后:

if command -v given-command > /dev/null 2>&1; then
  echo given-command is available
else
  echo given-command is not available
fi


将给您ksh中第一个zsh的路径(在极端情况下除外),无论command -ev是否也恰好是shell内置/别名/功能。

在其他shell中,您更喜欢:



zsh:yashwhatis -prc


ksh,zsh:akanga


yash:which


rc,阿肯加语:tcsh(注意带空格的路径)

鱼:zsh


请注意,如果只需运行which命令,就不必获取其路径,只需执行以下操作即可:

echo=$(which echo)


例如csh,以防止使用内置的echo

env echo this is not echoed by the builtin echo


当您需要外部命令时

另一种情况下,您可能想要在实际需要外部命令时使用$PATH。 POSIX要求所有shell内置函数(如echo)也可以作为外部命令使用,但是不幸的是,在许多系统上,echo==echo并非如此。例如,很少会在基于Linux的操作系统上找到echo=$commands[echo]命令,而大多数操作系统都有echo=${${:-echo}:c}命令(尽管不同的操作系统具有不同的选项和行为)。无需调用POSIX shell即可执行命令的任何地方。

C或各种语言的echo=$(whence -p echo)echo=$(command -ev echo) ...函数确实会调用shell来解析该命令行,因此echo=`whatis -p echo`确实可以在他们。 set echo (type -fp echo)就是一个例外,如果它看不到任何外壳特殊字符(空格除外),则会优化外壳。这也适用于它的反引号运算符:通过使用echo,您将不必使用该技巧。

评论


@Joe,这是许多商业Unices上的csh脚本。原因是历史原因,这就是为什么我给出了历史,所以人们了解了它的起源,为什么人们习惯了它,以及为什么实际上没有理由要使用它。是的,有些人使用(t)csh。并非所有人都使用Linux

–StéphaneChazelas
2013年8月3日7:39



阅读这篇文章后,我发现了很多答案的内容,但没有找到答案本身。它在这篇文章中的实际位置上说了为什么不使用它,而不是您可能尝试使用的方法,历史记录,实现方式,执行相关任务的其他命令或实际使用原因?为什么其他命令更好?他们与哪个有什么不同?他们如何避免它的陷阱?实际上,这个答案在替代方案的问题上比在问题上花费了更多的时间。

– user2357112支持Monica
2014年8月8日在12:24

命令由POSIX描述。

–卡兹
16年3月3日,下午2:06

@StéphaneChazelas如果我通过触摸/ usr / bin / mytestfile创建新文件,然后运行-v mytestfile命令,它将给出路径(而mytestfile没有)。

– jarno
17年7月2日在20:30

@jarno,哦,是的,你是对的。如果bash找不到可执行文件,它将在一个无法执行的文件上运行,因此它是“ OK”的(尽管实际上,该命令宁愿命令-v / type返回一个错误),因为这是它试图在执行时执行的命令您运行mytestfile,但破折号行为是有问题的,好像在可执行文件之前有一个不可执行的cmd,命令-v在执行cmd时将返回不可执行的cmd将执行该可执行文件(错误的也被散列了) 。 FreeBSD sh(也基于ash)具有相同的错误。 zsh,yash,ksh,mksh,bash和sh都可以。

–StéphaneChazelas
17年7月3日在5:59



#2 楼

已经解释了为什么不希望使用which的原因,但是这里是which实际失败的一些系统上的一些示例。

在类似Bourne的shell上,我们正在比较输出which的输出与type的输出(type是内置的shell,这是地面真理,因为shell告诉我们它将如何调用命令)。

许多情况是极端情况,但请记住,which / type经常在极端情况下使用(以查找意外行为的答案,例如:为什么该命令的行为如此,我叫哪个?)。

大多数系统,大多数类似于Bourne的外壳:函数

最明显的情况是函数:

$ type ls
ls is a function
ls ()
{
[ -t 1 ] && set -- -F "$@";
command ls "$@"
}
$ which ls
/bin/ls


原因仅是which报告可执行文件,有时还报告别名(尽管不是外壳程序的别名),而不是函数的报告。 rgot引用$@)有关如何使用它来报告功能的示例,但就像别名一样,因为它没有实现shell语法解析器,所以很容易上当: >
大多数系统,大多数类似于Bourne的shell:builtins

另一个明显的情况是buildins或关键字,因为which作为外部命令无法知道您的shell拥有哪些buildins诸如zshbashksh之类的壳可以动态加载内置函数):

$ 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;
}


(不适用于内置zshwhich

Solaris 10,AIX 7.1,HP / UX 11i,Tru64 5.1以及许多其他产品:

$ 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


这是因为在大多数商业Unices上,which(与原始实现类似)在3BSD上)是一个csh脚本,其内容为~/.cshrc
它将报告的别名是在那里定义的别名,无论您当前定义的别名是什么,也无论您实际使用的外壳如何。

在HP / UX或Tru64中: />
$ 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


(Solaris和AIX版本通过在阅读$path之前保存~/.cshrc并在查找命令之前将其还原来解决该问题)

% echo 'setenv PATH /bin:/usr/bin' >> ~/.cshrc
% setenv PATH ~/bin:/bin:/usr/bin
% ln -s /bin/ls ~/bin/
% which ls
/bin/ls


或:

$ 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


(当然,作为csh脚本,您不能指望它可以与包含空格的参数一起使用...)

CentOS 6.4,bash

$ 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


在该系统上,有一个系统范围内定义的别名,用于包装GNU which命令。 >
伪造的输出是因为which读取bashalias的输出,但不知道如何正确解析它并使用试探法(每行一个别名,在|;之后寻找第一个找到的命令, & ...)

最糟糕的事情在CentOS上,zsh具有完美的which内置命令,但CentOS设法通过用GNU which的无效别名替换它来破坏它。 />(尽管适用于大多数具有许多shell的系统)

$ 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='


在Debian上,/bin/which/bin/sh脚本。就我而言,shdash,但在bash时是相同的。

未设置的PATH并非禁用PATH查找,而是意味着使用系统的默认PATH,不幸的是,在Debian上,没有人同意(dashbash具有/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binzsh具有/bin:/usr/bin:/usr/ucb:/usr/local/binksh93具有/bin:/usr/binmksh具有/usr/bin:/bin$(getconf PATH)),execvp()(与env类似)具有:/bin:/usr/bin(是,在当前目录中先查找!)。
which在上面出错的原因,因为它使用的是dash的默认PATH,这与ksh93的默认

使用GNU which报告以下情况并不太好:

$ unset PATH
$ which which
/usr/local/bin/which
$ type which
which is a tracked alias for /bin/which


(有趣的是,我的系统上确实有一个/usr/local/bin/which,实际上是akanga附带的akanga脚本(一个rc shell派生,其中默认PATH/usr/ucb:/usr/bin:/bin:.))

bash,任何系统:

一个克里斯在他的答案中指的是:

which: no which in ((null))


还手动调用了hash之后:

$ 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


现在,which有时甚至是type失败的情况是:

$ 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)


现在,带有一些外壳:

$ 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


和其他外壳:

$ foo
bash: ./b/foo: /: bad interpreter: Permission denied


whichtype都无法事先知道无法执行b/foo。当调用bash时,某些壳(例如kshyashfoo)确实会尝试运行b/foo并报告错误,而其他壳(例如zshashcshBournetcsh)将在q4312079发生故障时调用a/foo

评论


mksh实际上对默认的$ PATH使用了不同的东西:首先,使用操作系统的编译时常量_PATH_DEFPATH(最常见的是在BSD上),然后使用confstr(_CS_PATH,…)(POSIX),如果两者都没有,如果存在或失败,则使用/ bin:/ usr / bin:/ sbin:/ usr / sbin。

– mirabilos
2014-2-27 14:26

在您的第一个示例中,即使ls是一个函数,它也会使用PATH中的ls。最好告诉您使用的是/ usr / bin / ls还是/ usr / local / bin / ls。我看不到“为什么不使用哪个”。

– rudimeier
17年5月3日在13:12

@rudimeier,无论ls函数调用/ bin / ls还是/ opt / gnu / bin / ls或dir或根本不执行任何操作,ls都会给我/ bin / ls。 IOW,它(哪些实现,IMMV)给出了不相关的内容

–StéphaneChazelas
17年5月3日在13:43



@StéphaneChazelas。不不不。我已经知道我的ls是一个函数。我知道我的ls函数正在从PATH调用ls。现在告诉我文件在哪里。您仅看到一个用例:“我的外壳将使用此命令做什么”。对于这个错误的用例,请更正。但是,在其他一些用例中,(GNU)正是正确的选择。

– rudimeier
17年5月3日在14:18

@rudimeter,取决于哪个实现。有人会告诉您这是一个别名(如果您配置了别名,或者您家中有一个〜/ .cshrc都具有这样的别名),有人会告诉您一条路径,但在某些情况下会提供错误的路径。 sh -c'command -v ls',尽管不是很完美,但仍然更有可能为您提供不同要求的正确答案(也是标准)。

–StéphaneChazelas
17年5月3日在15:06



#3 楼

从我的快速浏览中看来,Stephane并未提及的一件事是which对shell的路径哈希表一无所知。这样做的结果是,它可能会返回不代表实际运行结果的结果,这使其在调试中无效。

#4 楼

我通常会在不建议用户使用此问题时感到畏缩,因为对which进行无基础的重击对任何人都没有用。好吧,为什么应该禁止which
问题应该是,哪个运行良好,并且可以完成特定的工作?
其中一个,/ bin /中的外部实用程序在Debian中是一个外壳程序该脚本的目标只是在路径上列出给定名称的可执行文件。我相信which正确完成了预期的目标。它没有加载别名,没有函数,没有从外壳程序加载任何东西,只是在PATH上列出了给定名称的第一个(或所有)可执行文件。用户应该自己弄清与给定名称相同的文件的含义。
是的,其他which实现可能(并且通常确实)存在特定问题。

#5 楼

我认为问题的答案重新。如果which未能提及缓存在结果中扮演的角色,则说明使用不完整。另外:我习惯使用which,似乎无法回忆起hash -rrehash命令。我对which的寻求帮助不可避免地会在列表的顶部或附近返回此问答。
作为参考,我在Linux机器上使用bash,在macOS中使用zsh。正如评论中指出的那样:

bash中:which/usr/bin/which

zsh中:which是内置的shell


相反据我的直觉,以内置方式运行会给在which中使用zsh带来不利的一面-可以通过另一个内置的hash来纠正的不利之处。每次调用时进行新搜索。尽管这在计算上很有效,但要在及时性上进行权衡。例如,您刚刚安装了一个名为which的新工具,并且需要/想知道它的安装位置。例如,将带有MacPorts的mynewtool用作macOS中的软件包管理器:
 zsh 

显然是错误的答案。要解决此问题,必须更新/刷新缓存:
 % sudo port install mynewtool
...
% which mynewtool
mynewtool not found
% 
 

% hash -r % which mynewtool /usr/local/bin/mynewtool 选项导致-r重置或清除整个缓存
hash不能像q4​​312079q那样容易地在help hash中使用,但是您可能会发现此问题解答对解决此问题很有帮助。
我将在此处停止,因为此答案可能被认为与OP的问题无关由一些。如果您需要更多细节或zsh的替代品,请使用以下有用的一对:1、2。

评论


bash shell没有内置的which,因此不会有缓存问题。

– Kusalananda♦
12月24日20:35

@Kusalananda:你是对的……我显然将我的zsh体验换成了bash体验。我知道这仅是zsh的内置函数,但假定由于哈希是内置的,因此这两个缓存问题与这两个都相关。我想这是在学习困难的方法:)现在,我想知道为什么zsh在这种情况下为什么要构建哪个内置函数?再次感谢-我将尽快编辑答案。

– Seamus
12月24日21:16

#6 楼


我们经常听到应该避免的情况。为什么?我们应该改用什么呢?


我从未听说过。请提供具体示例。我会担心您的Linux发行版和安装的软件包,因为这是which的来源! bash版本3.2-147中的
> which which

which: shell built-in command.


> 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.

由Linux内核组织分发的软件包,用作Linux操作系统的一部分。它还提供了其他文件

我的which是2.19版。发行说明可以轻松地追溯到v2.13(2007年8月28日)。不知道这是什么目的或目标,那件冗长的事经331次投票肯定没有得到回答。

评论


请注意,这个问题没有提及它所指的Unix。 Linux只是其中之一。

– Kusalananda♦
19年2月5日在23:02

如您的-v所示,那是GNU(在另一个答案中提到的最奢侈的一个,绝不是Linux特有的),而不是util-linux,而AFAIK从未包含过which实用程序。 util-linux 2.19来自2011,GNU 2.19来自2008。

–StéphaneChazelas
19年2月6日在6:57