cd
是内置于POSIX的 shell:
如果一个简单的命令产生一个命令名和一个可选的参数列表,则应执行以下动作:
- 如果命令名称不包含任何斜杠,则应按以下顺序执行第一个成功的步骤:
...
- 如果命令名称与下表中列出的实用程序名称匹配,则应调用该实用程序。
...
cd
...
- 否则,应使用PATH ...搜索该命令。
尽管这并没有明确说明它必须是内置的,但该规范在以下描述中cd
继续说:
由于cd影响当前的shell执行环境,因此它总是作为shell常规内置文件提供。
从bash
手册:
以下shell内置命令是从Bourne Shell继承的。这些命令按照POSIX标准指定的方式实现。
...
cd
cd [-L|[-P [-e]]] [directory]
我想您可以想到一种cd
不必内置的体系结构。但是,您必须了解内置的含义。如果您在外壳程序中编写特殊代码以对某条命令执行某些操作,则您将接近内置函数。您做得越多,拥有内置功能越好。
例如,您可以让外壳程序具有IPC与子进程进行通信,并且将有一个cd
程序来检查目录的存在以及您是否具有访问权限,然后与外壳程序进行通信以告诉它更改其目录。目录。但是,您必须检查与您通信的进程是否是子进程(或仅与子进程进行特殊的通信方式,例如特殊的文件描述符,共享内存等),以及该进程是否确实存在。运行受信任的cd
程序或其他内容。那就是一堆蠕虫。
或者,您可以使用一个cd
程序来进行chdir
系统调用,然后使用所有当前环境变量将新的外壳程序应用于新外壳程序,然后在完成后杀死其父外壳程序(以某种方式)。1个
更糟糕的是,您甚至可能拥有一个进程可以更改其他进程环境的系统(我认为从技术上讲,您可以使用调试器来完成此任务)。但是,这样的系统将非常非常脆弱。
您会发现自己添加了越来越多的代码来保护此类方法,并且简单地将其内置即可。
某些东西是可执行文件并不妨碍它成为内置文件。例子:
echo
和 test
echo
和test
被POSIX授权的实用工具(/bin/echo
和/bin/test
)。但是几乎每个流行的外壳都有内置的echo
和内置的test
。同样,kill
也内置了作为程序可用的内置函数。其他包括:
sleep
(不常见)
time
false
true
printf
但是,在某些情况下,命令只能是内置命令。其中之一是cd
。通常,如果未指定完整路径,并且命令名称与内置命令的名称匹配,则会调用适合该命令的函数。根据不同的外壳,内置的行为和可执行的可能不同(这是特别一个为问题echo
,其中有大相径庭的行为。如果你想成为某些行为的,最好是可执行文件使用调用完整路径,并设置类似变量POSIXLY_CORRECT
(即使这样也没有真正的保证)。
从技术上讲,没有什么可以阻止您提供既是外壳又将每个命令都内置的操作系统。接近这个极端的是整体式BusyBox。BusyBox是一个二进制文件,它(取决于调用的名称)可以充当240多个程序中的任何一个,包括Almquist Shell(ash
)。如果您PATH
在运行BusyBox时取消设置ash
,则仍可访问BusyBox中可用的程序,而无需指定PATH
。它们几乎是shell内置的,除了shell本身是BusyBox的某种内置。
如果您查看dash
源代码,那么执行线程就是这样的(当然,使用管道和其他内容时还会涉及其他功能):
main
→交通cmdloop
→交通evaltree
→交通evalcommand
evalcommand
然后使用findcommand
确定命令是什么。如果是内置的,则:
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;
}
本节中所有指向源代码的链接都是基于行号的,因此它们可能会更改,恕不另行通知。
1 POSIX系统确实具有cd
可执行文件。
边注:
在Unix和Linux上有很多关于shell行为的优秀文章。特别是:
如果您到目前为止还没有注意到问题的模式,那么几乎所有问题都涉及StéphaneChazelas。
type
命令