Warum funktioniert das `which`-Kommando für` cd` nicht? Ich kann die ausführbare Datei für `cd` auch nicht finden!

30

Ich habe es versucht which cdund es hat keinen Pfad angegeben, sondern den Exit-Code 1 zurückgegeben (überprüft mit echo $?). Das Coreutil cdselbst funktioniert, also sollte die ausführbare Datei dort sein, oder? Ich habe auch eine findfor ausgeführt cd, aber es wurde keine ausführbare Datei angezeigt. Wie wird es dann umgesetzt?

Aktualisieren:

Ich weiß nicht, ob ich das in einem anderen Beitrag fragen soll, aber da ich denke, dass es hier gut ist, erweitere ich den Beitrag (?) ... Die Antwort war also eigentlich ganz einfach, es gibt keine ausführbare Datei dafür - weil es ist a builtin - Aber ich habe festgestellt, dass einige builtins (bash shell in Fedora) die ausführbaren Dateien haben! Also eingebaut -> keine ausführbare Datei stimmt wohl nicht? Vielleicht eine Antwort, die erklärt, was Builtins eigentlich sind (Builtin-Befehle?), Und die hier eigentlich die Angelegenheit ist, anstatt sich mehr darauf zu konzentrieren cd... Einige gute Links, die zuvor gepostet wurden, weisen darauf hin, dass Builtins keine Programme sind ... also, was sind sie? Wie arbeiten Sie? Sind sie nur Funktionen oder Threads der Shell?

präzise
quelle
1
Lesen Sie diese Antwort. Es wird auf die Verwendung vorgeschlagen typeBefehl
c0rp
7
In dieser cdFrage und Antwort erfahren Sie, warum eine eingebaute CD sein muss: Warum ist CD kein Programm? und dieses auf warum typeist besser als which: Warum nicht "welches" verwenden? Was ist dann zu verwenden?
Terdon
Ähnliche Frage hier: askubuntu.com/q/613470/178596
Wilf

Antworten:

46

Befehl cdkann keine ausführbare Datei sein

In einer Shell cdwird verwendet, um "in ein anderes Verzeichnis zu wechseln" oder formeller, um das aktuelle Arbeitsverzeichnis (CWD) zu ändern. Es ist unmöglich, das als externen Befehl zu implementieren:

Das Verzeichnis gehört zu einem Prozess

Das aktuelle Arbeitsverzeichnis ist das Verzeichnis, in dem relative Pfade interpretiert werden, um einen vollständigen Pfad zu erhalten, mit dem auf Dateien zugegriffen werden kann. Relative Pfade werden an vielen Stellen verwendet, und die Interpretation in einem Prozess sollte keinen Einfluss auf einen anderen Prozess haben.
Aus diesem Grund hat jeder Prozess ein eigenes aktuelles Arbeitsverzeichnis.

cdEs geht beispielsweise darum, das aktuelle Arbeitsverzeichnis des Shell-Prozesses zu ändern bash.

Wenn es sich um einen externen Befehl handeln würde, würde eine ausführbare Datei im Pfad, die diese ausführbare Datei ausführt, einen Prozess mit einem eigenen Arbeitsverzeichnis erstellen, ohne das der aktuellen Shell zu beeinflussen. Selbst wenn der externe Befehl das Verzeichnis ändern würde, wird diese Änderung nicht mehr ausgeführt, wenn der externe Prozess beendet wird.

Shell eingebaute Befehle

Es macht also keinen Sinn, einen externen Befehl für die Aufgabe von auszuführen cd. Der Befehl cdmuss eine Änderung auf den aktuell ausgeführten Shell-Prozess anwenden.

Dazu ist es ein "eingebauter Befehl" der Shell.

Builtin-Befehle sind Befehle, die sich ähnlich wie externe Befehle verhalten, jedoch in der Shell implementiert sind (also cdnicht Teil der coreutils sind). Auf diese Weise kann der Befehl den Status der Shell selbst ändern, in diesem Fall chdir()see (see man 2 chdir) aufrufen .

Über which

Die Antwort auf die Titelfrage ist nun einfach:
Der ausführbare Befehl whichkann uns nicht sagen, dass cd ein eingebauter Befehl ist, da ein ausführbarer Befehl nichts über eingebaute Befehle weiß.

Alternative type -a

Als Alternative zu whichkönnen Sie verwenden type -a; Es können ausführbare Befehle und integrierte Befehle angezeigt werden. Zusätzlich werden Aliase und Funktionen angezeigt - ebenfalls in der Shell implementiert:

$ 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
Volker Siegel
quelle
1
Tolle Erklärung!
SaltyNuts
3
Viel besser als die derzeit akzeptierte Antwort - dies erklärt, warum cd eine Shell eingebaut ist.
Lily Chung
28

cdist eine POSIX- mandated Shell eingebaut:

Wenn ein einfacher Befehl einen Befehlsnamen und eine optionale Liste von Argumenten ergibt, müssen die folgenden Aktionen ausgeführt werden:

  1. Wenn der Befehlsname keine Schrägstriche enthält, wird der erste erfolgreiche Schritt in der folgenden Reihenfolge ausgeführt:
    ...
    • Wenn der Befehlsname mit dem Namen eines in der folgenden Tabelle aufgelisteten Dienstprogramms übereinstimmt, wird dieses Dienstprogramm aufgerufen.
      ...
      cd
      ...
    • Andernfalls soll der Befehl mit PATH gesucht werden ...

Dies bedeutet zwar nicht ausdrücklich, dass es sich um ein eingebautes Produkt handeln muss, die Spezifikation geht jedoch in die Beschreibung voncd :

Da sich cd auf die aktuelle Shell-Ausführungsumgebung auswirkt, wird es immer als reguläre integrierte Shell bereitgestellt.

Aus dem bashHandbuch :

Die folgenden in die Shell eingebauten Befehle werden von der Bourne-Shell geerbt. Diese Befehle werden gemäß dem POSIX-Standard implementiert.
...

cd
       cd [-L|[-P [-e]]] [directory]

Ich nehme an, Sie könnten sich eine Architektur cdvorstellen, bei der es sich nicht unbedingt um eine eingebaute Architektur handeln muss. Sie müssen jedoch sehen, was ein eingebautes impliziert. Wenn Sie speziellen Code in die Shell schreiben, um etwas für einen bestimmten Befehl zu tun, haben Sie fast ein eingebautes Programm. Je mehr Sie tun, desto besser ist es, einfach ein eingebautes zu haben.

Beispielsweise könnte die Shell IPC für die Kommunikation mit Unterprozessen haben, und es würde ein cdProgramm geben, das prüft, ob das Verzeichnis vorhanden ist und ob Sie die Zugriffsberechtigung dafür haben, und kommuniziert dann mit der Shell, um ihr anzuweisen, das Verzeichnis zu ändern Verzeichnis. Sie müssen dann jedoch überprüfen, ob der mit Ihnen kommunizierende Prozess ein untergeordnetes Element ist (oder nur spezielle Kommunikationsmittel wie einen speziellen Dateideskriptor, einen gemeinsam genutzten Speicher usw. bereitstellen) und ob der Prozess tatsächlich ausgeführt wird Ausführen des vertrauenswürdigen cdProgramms oder etwas anderes. Das ist eine ganze Dose Würmer.

Oder Sie könnten ein cdProgramm haben, das den chdirSystemaufruf ausführt und eine neue Shell mit allen aktuellen Umgebungsvariablen startet, die auf die neue Shell angewendet werden, und dann die übergeordnete Shell (irgendwie) beendet, wenn dies erledigt ist. 1

Schlimmer noch, Sie könnten sogar ein System haben, in dem ein Prozess die Umgebung anderer Prozesse verändern kann (technisch gesehen können Sie dies mit Debuggern tun). Ein solches System wäre jedoch sehr, sehr anfällig.

Sie werden feststellen, dass Sie immer mehr Code hinzufügen, um solche Methoden abzusichern, und es ist erheblich einfacher, ihn einfach zu einem eingebauten Code zu machen.


Dass etwas eine ausführbare Datei ist, hindert es nicht daran, eingebaut zu sein. Ein typisches Beispiel:

echo und test

echound testsind von POSIX beauftragte Dienstprogramme ( /bin/echound /bin/test). Dennoch hat fast jede populäre Muschel ein eingebautes echound test. Ebenso killist auch eingebaut, was als Programm zur Verfügung steht. Andere sind:

  • sleep (nicht so häufig)
  • time
  • false
  • true
  • printf

Es gibt jedoch einige Fälle, in denen ein Befehl nur ein integrierter Befehl sein kann. Eine davon ist cd. Wenn der vollständige Pfad nicht angegeben wird und der Befehlsname mit dem eines integrierten Befehls übereinstimmt, wird in der Regel eine für diesen Befehl geeignete Funktion aufgerufen. In Abhängigkeit von der Schale, das Verhalten des builtin und dass die ausführbaren Datei unterscheiden kann (dies ist vor allem ein Problem fürecho die , die hat wild unterschiedliche Verhaltensweisen . Wenn Sie bestimmt das Verhalten sein will, ist es vorzuziehen , die ausführbare Datei mit der rufen vollständiger Pfad und setze Variablen wie POSIXLY_CORRECT(auch dann gibt es keine wirkliche Garantie).

Technisch gesehen hindert Sie nichts daran, ein Betriebssystem bereitzustellen, das auch eine Shell ist und in dem jeder Befehl integriert ist. Nahe an diesem äußersten Ende befindet sich die monolithische BusyBox . BusyBox ist eine einzelne Binärdatei, die sich (abhängig vom Namen, mit dem sie aufgerufen wird) wie eines von über 240 Programmen verhalten kann , einschließlich einer Almquist-Shell ( ash). Wenn Sie die Einstellung PATHwährend der Ausführung der BusyBox aufheben ash, können Sie weiterhin auf die in BusyBox verfügbaren Programme zugreifen, ohne a anzugeben PATH. Sie sind beinahe Shell-Builtins, mit der Ausnahme, dass die Shell selbst eine Art Builtin für BusyBox ist.


Fallstudie: Die Debian-Almquist-Shell ( dash)

Wenn Sie sich die dashQuelle ansehen, sieht der Ausführungsthread ungefähr so ​​aus (natürlich mit zusätzlichen Funktionen, wenn Pipes und andere Dinge verwendet werden):

maincmdloopevaltreeevalcommand

evalcommandwird dann verwendet findcommand, um zu bestimmen, was der Befehl ist. Wenn es ein eingebautes ist, dann :

 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.cmdist struct( struct builtincmd), eine von deren Mitglieder ein Funktionszeiger, mit einer Signatur typisch main: (int, char **). Die evalbltinFunktion ruft (abhängig davon, ob der eingebaute evalBefehl der Befehl ist oder nicht) entweder evalcmddiesen Funktionszeiger auf. Die eigentlichen Funktionen sind in verschiedenen Quelldateien definiert. echoZum Beispiel ist :

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

Alle Links zum Quellcode in diesem Abschnitt basieren auf Zeilennummern. Sie können sich daher ohne vorherige Ankündigung ändern.


1 POSIX-Systeme haben eine cdausführbare Datei .


Randnotiz:

Unter Unix & Linux gibt es eine Menge exzellenter Beiträge, die sich mit dem Shell-Verhalten befassen. Bestimmtes:

Wenn Sie in den bisher aufgeführten Fragen kein Muster bemerkt haben, sind fast alle von Stéphane Chazelas betroffen .

muru
quelle
4
Beachten Sie, dass Sie den Hilfetext cdmit help cd(dasselbe für alle Shell-Builtin-Befehle)
Sylvain Pineau
@ SylvainPineau Obwohl ich auf das Bash-Handbuch verwiesen habe, ist dieser Rat nicht generell auf andere Shells wie zsh anwendbar.
muru
In der Tat helpist eine Bash gebaut (für zsh, es ist run-help cd)
Sylvain Pineau
Die verknüpfte Beschreibung aus der POSIX-Spezifikation besagt nicht explizit, dass cddies als integrierte Shell erfolgen muss. Es cdist jedoch die einzige unkomplizierte Implementierung , die darauf basiert, wie Prozesseigenschaften und deren Übertragung in UNIX als integrierte Shell funktionieren . Siehe die Antwort von Volker Siegel .
Pabouk
@pabouk in der Tat (es nennt es ein Dienstprogramm) und fährt dann fort: "Da cd die aktuelle Shell-Ausführungsumgebung beeinflusst, wird es immer als reguläres eingebautes Shell-Programm bereitgestellt."
muru
8

Sie können keine ausführbare Datei für finden, cdda es keine gibt.

cdist ein interner Befehl Ihrer Shell (zB bash).

Uwe Plonus
quelle
7

von man which:

Dies gibt die Pfadnamen der Dateien (oder Links) zurück, die in der aktuellen Umgebung ausgeführt werden würden, wenn die Argumente als Befehle in einer streng POSIX-konformen Shell angegeben worden wären. Dazu durchsucht es den Pfad nach ausführbaren Dateien, die mit den Namen der Argumente übereinstimmen. Es folgen keine symbolischen Links.

Wie aus der Beschreibung von hervorgeht which, handelt es sich nur um eine Überprüfung PATH. Wenn Sie also einige implementiert haben bash function, wird Ihnen nichts angezeigt. Es ist besser, typeBefehl zusammen mit zu verwenden which.

Zum Beispiel in Ubuntu lsBefehl Alias ​​auf ls --color=auto.

$ type ls
ls is aliased to `ls --color=auto'

$ which ls
/bin/ls

Und wenn Sie Testfunktion implementieren hello:

$ function hello() { for i in {1,2,3}; do echo Hello $i;done }
$ which hello

whichzeigt nichts. Aber type:

$ type hello
hello is a function
hello () 
{ 
    for i in {1,2,3};
    do
        echo Hello $i;
    done
}

In Ihrem Fall:

$ type cd
cd is a shell builtin

Dies bedeutet, dass cdes sich um eine eingebaute Shell handelt , die sich im Inneren befindet bash. Alle Bash-Befehle man bash, die in Abschnitt SHELL BUILTIN COMMANDS beschrieben sind

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.
c0rp
quelle
2
Mmmm,manwhich .
IQAndreas
1
Es sollte vielleicht mehr betont werden: Nicht verwenden which, verwenden type.
Tripleee