问题描述
我尝试了which cd
,它没有给出路径,而是返回退出代码1(用echo $?
检查)。 coreutil cd
本身正在工作,所以可执行文件应该在那里,对吧?我还为cd
运行了find
,但没有显示可执行文件。那怎么实现呢?
更新:
我不知道我是否应该在另一篇文章中提出这个问题,但由于我认为这里很好,我正在扩展(?)帖子……所以答案其实很简单,没有可执行文件 – 因为它是内置 – 但我发现一些内置(Fedora中的bash shell)有可执行文件!所以内置 – >我想,没有可执行文件是不对的?也许答案解释了内置实际上是什么(内置命令?),这实际上是这里的问题,而不是更多地关注cd
…之前发布的一些好的链接表明内置不是程序……那么它们是什么?他们是如何工作的?它们只是shell的函数或线程吗?
最佳解决方案
命令cd
不能是可执行文件
在shell中,cd
用于“进入另一个目录”,或者更正式地用于更改curent工作目录(CWD)。作为外部命令实现它是不可能的:
该目录属于进程
curent工作目录是用于解释相对路径以获取可用于访问文件的完整路径的目录。相对路径在许多地方使用,一个过程中的解释不应影响另一个过程。因此,每个进程都有自己当前的工作目录。
cd
是关于更改shell进程的当前工作目录,例如bash
。
如果它是一个外部命令,路径中的可执行文件,运行该可执行文件将创建一个具有自己的工作目录的进程,而不会影响当前shell的进程。即使外部命令会改变它的目录,当外部进程退出时,该更改也会消失。
Shell内置命令
因此,为cd
的任务运行外部命令是没有意义的。命令cd
需要对当前运行的shell进程应用更改。
为此,它是shell的“builtin command”。
内置命令是与外部命令类似的命令,但是在shell中实现(因此cd
不是coreutils的一部分)。这允许命令改变shell本身的状态,在这种情况下调用chdir()
参见(参见man 2 chdir
);
关于which
现在,标题问题的答案很简单:可执行命令which
无法告诉我们cd是内置命令,因为可执行命令对内置命令一无所知。
替代type -a
作为which
的替代品,您可以使用type -a
;它可以看到可执行命令和内置函数;另外,它会看到别名和函数 – 也在shell中实现:
$ type -a cd
cd is a shell builtin
$ type -a type
type is a shell builtin
$ type -a which
which is /usr/bin/which
which is /bin/which
次佳解决方案
cd
是一个内置的POSIX-mandated shell:
If a simple command results in a command name and an optional list of arguments, the following actions shall be performed:
- If the command name does not contain any slashes, the first successful step in the following sequence shall occur:
…
- If the command name matches the name of a utility listed in the following table, that utility shall be invoked.
…
cd
…- Otherwise, the command shall be searched for using the PATH…
虽然这没有明确说明它必须是内置,但规范接着说,在cd
的描述中:
Since cd affects the current shell execution environment, it is always provided as a shell regular built-in.
从bash
手册:
The following shell builtin commands are inherited from the Bourne Shell. These commands are implemented as specified by the POSIX standard.
…cd cd [-L|[-P [-e]]] [directory]
我想你可以想到一个架构,其中cd
不一定是内置的。但是,你必须看看内置意味着什么。如果你在shell中编写特殊代码来为某个命令做一些事情,你就会接近内置。你做的越多,只需要内置就越好。
例如,你可以让shell有IPC与子进程通信,并且会有一个cd
程序,它会检查目录是否存在以及你是否有权访问它,然后与shell通信以告诉它改变它的目录。但是,您必须检查与您通信的进程是否是子进程(或者只与子进程通信的特殊方式,例如特殊文件描述符,共享内存等),如果进程实际上是运行受信任的cd
程序或其他东西。这是一整套蠕虫。
或者您可以使用cd
程序进行chdir
系统调用,并启动一个新shell,其中所有当前环境变量都应用于新shell,然后在完成时以某种方式终止其父shell。
更糟糕的是,您甚至可以拥有一个系统,其中一个进程可以改变其他进程的环境(我认为从技术上讲,您可以使用调试器来完成此操作)。然而,这样的系统将非常非常脆弱。
您将发现自己添加了越来越多的代码来保护这些方法,而简单地将其作为内置函数则相当简单。
某些东西是可执行文件并不能阻止它成为内置函数。例证:
echo
和test
echo
和test
是POSIX-mandated用途(/bin/echo
和/bin/test
)。然而,几乎所有流行的 shell 都有内置的echo
和test
。同样,kill
也是内置的,可作为程序使用。其他包括:
-
sleep
(不常见) -
time
-
false
-
true
-
printf
但是,在某些情况下,命令不能是内置命令。其中之一是cd
。通常,如果未指定完整路径,并且命令名称与内置命令名称匹配,则调用适合该命令的函数。取决于shell,内置的行为和可执行文件的行为可能不同(对于具有wildly differing behaviours的echo
,这尤其是个问题。如果您想确定行为,最好使用完整的调用可执行文件路径,并设置像POSIXLY_CORRECT
的变量(即使那时没有真正的保证)。
从技术上讲,没有什么可以阻止你提供一个也是一个shell的操作系统,并且每个命令都是内置的。接近这个极端的是单片BusyBox。 BusyBox是一个单独的二进制文件(取决于它所调用的名称)可以表现为任何over 240 programs,包括Almquist Shell(ash
)。如果在运行BusyBox ash
时取消设置PATH
,则仍可访问BusyBox中可用的程序,而无需指定PATH
。它们接近于shell内置,除了shell本身是内置于BusyBox的sort-of。
案例研究:Debian Almquist Shell(dash
)
如果查看dash
源代码,执行线程就是这样的(当然,在使用管道和其他东西时还会涉及其他功能):
main
→cmdloop
→evaltree
→evalcommand
然后,evalcommand
使用findcommand
来确定命令是什么。如果它是内置的,then:
case CMDBUILTIN:
if (spclbltin > 0 || argc == 0) {
poplocalvars(1);
if (execcmd && argc > 1)
listsetvar(varlist.list, VEXPORT);
}
if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) {
if (exception == EXERROR && spclbltin <= 0) {
FORCEINTON;
break;
cmdentry.u.cmd
是struct
(struct builtincmd
),其成员之一是功能指针,具有典型的main
:(int, char **)
的特征。 evalbltin
函数调用(取决于内置函数是否为eval
命令)evalcmd
或此函数指针。实际功能在各种源文件中定义。例如,echo
是:
int
echocmd(int argc, char **argv)
{
int nonl;
nonl = *++argv ? equal(*argv, "-n") : 0;
argv += nonl;
do {
int c;
if (likely(*argv))
nonl += print_escape_str("%s", NULL, NULL, *argv++);
if (nonl > 0)
break;
c = *argv ? ' ' : '\n';
out1c(c);
} while (*argv);
return 0;
}
本节中源代码的所有链接均为行number-based,因此它们可能会更改,恕不另行通知。
1 POSIX系统具有cd
可执行文件。
边注:
在Unix和Linux上有很多优秀的帖子。处理shell行为的Linux。特别是:
-
Why can’t I redirect a path name output from one command to “cd”?
-
cd
外部命令有什么意义? -
Is there a difference between prepending a name-value-pair to a command and using env in bash?
If you haven’t noticed a pattern in the questions listed so far, nearly all of them involve Stéphane Chazelas.
第三种解决方案
您找不到cd
的可执行文件,因为没有。
cd
是shell的内部命令(例如bash
)。
第四种方案
来自man which
:
which returns the pathnames of the files (or links) which would be executed in the current environment, had its arguments been given as commands in a strictly POSIX-conformant shell. It does this by searching the PATH for executable files matching the names of the arguments. It does not follow symbolic links.
从which
的描述可以看出,它只是检查PATH
。因此,如果您实施了一些bash function
,它将不会显示任何内容。最好使用type
命令和which
。
例如,在Ubuntu ls
命令中别名为ls --color=auto
。
$ type ls
ls is aliased to `ls --color=auto'
$ which ls
/bin/ls
如果您实现测试功能hello
:
$ function hello() { for i in {1,2,3}; do echo Hello $i;done }
$ which hello
which
什么都没有显示。但是type
:
$ type hello
hello is a function
hello ()
{
for i in {1,2,3};
do
echo Hello $i;
done
}
在你的情况下:
$ type cd
cd is a shell builtin
这意味着cd
是shell builtin,它位于bash
内。所有bash builtins在man bash
中描述,在SHELL BUILTIN命令中
SHELL BUILTIN COMMANDS
Unless otherwise noted, each builtin command documented in this section
as accepting options preceded by - accepts -- to signify the end of the
options. The :, true, false, and test builtins do not accept options
and do not treat -- specially. The exit, logout, break, continue, let,
and shift builtins accept and process arguments beginning with - with‐
out requiring --. Other builtins that accept arguments but are not
specified as accepting options interpret arguments beginning with - as
invalid options and require -- to prevent this interpretation.