当前位置: 首页>>技术问答>>正文


为什么`which`命令不能用于`cd`?我也找不到`cd`的可执行文件!

, ,

问题描述

我尝试了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:

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

更糟糕的是,您甚至可以拥有一个系统,其中一个进程可以改变其他进程的环境(我认为从技术上讲,您可以使用调试器来完成此操作)。然而,这样的系统将非常非常脆弱。

您将发现自己添加了越来越多的代码来保护这些方法,而简单地将其作为内置函数则相当简单。


某些东西是可执行文件并不能阻止它成为内置函数。例证:

echotest

echotest是POSIX-mandated用途(/bin/echo/bin/test)。然而,几乎所有流行的 shell 都有内置的echotest。同样,kill也是内置的,可作为程序使用。其他包括:

  • sleep(不常见)

  • time

  • false

  • true

  • printf

但是,在某些情况下,命令不能是内置命令。其中之一是cd。通常,如果未指定完整路径,并且命令名称与内置命令名称匹配,则调用适合该命令的函数。取决于shell,内置的行为和可执行文件的行为可能不同(对于具有wildly differing behavioursecho,这尤其是个问题。如果您想确定行为,最好使用完整的调用可执行文件路径,并设置像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源代码,执行线程就是这样的(当然,在使用管道和其他东西时还会涉及其他功能):

maincmdloopevaltreeevalcommand

然后,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.cmdstruct(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。特别是:

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

这意味着cdshell 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.

参考资料

本文由Ubuntu问答整理, 博文地址: https://ubuntuqa.com/article/6847.html,未经允许,请勿转载。