Warum kann ich einen von einem Befehl ausgegebenen Pfadnamen nicht auf "cd" umleiten?

27

Ich versuche cd, einen von einem anderen Befehl an ihn umgeleiteten Verzeichnisnamen zu akzeptieren. Keine dieser Methoden funktioniert:

$ echo $HOME | cd
$ echo $HOME | xargs cd

Das funktioniert:

$ cd $(echo $HOME)

Warum funktioniert der erste Befehlssatz nicht, und gibt es andere, die auf diese Weise ebenfalls fehlschlagen?

Jhonathan
quelle
Von anderen verweisen Sie auf andere Befehle oder andere Methoden zur Verwendung von CDs, die auf diese Weise fehlschlagen?
Didi Kohen
@ DavidKohen Ich verweise auf andere Befehle
Jhonathan
Einige bemerkenswerte Beispiele sind ulimit, umask, popd, pushd, set, export und read.
Didi Kohen

Antworten:

32

cdist kein externer Befehl - es ist eine in die Shell eingebaute Funktion. Es wird im Kontext der aktuellen Shell ausgeführt und nicht wie externe Befehle in einem Fork / Exec-Kontext als separater Prozess.

Ihr drittes Beispiel funktioniert, da die Shell die Variable und die Befehlssubstitution vor dem Aufrufen des cdeingebauten Befehls erweitert, sodass dieser cdden Wert von ${HOME}als Argument erhält .

POSIX - Systemen zu tun haben ein binäres cd- auf meinem FreeBSD - Maschine, es ist an /usr/bin/cd, aber es tut nicht das, was Sie denken. Das Aufrufen der Binärdatei cdbewirkt, dass die Shell die Binärdatei aufteilt / ausführt, wodurch ihr Arbeitsverzeichnis in den von Ihnen übergebenen Namen geändert wird. Sobald dies jedoch der Fall ist, wird die Binärdatei beendet und der Forked / Exec'd-Prozess verschwindet. Sie kehren zu Ihrer Shell zurück, die sich noch in dem Verzeichnis befindet, in dem sie sich vor dem Start befand.

D_Bye
quelle
10
Wodurch wundert man sich, wozu der externe cdBefehl gut ist?
Kojiro
23

cdliest keine Standardeingabe. Deshalb funktioniert Ihr erstes Beispiel nicht.

xargsbenötigt einen Befehlsnamen, dh einen Namen einer unabhängigen ausführbaren Datei. cdmuss ein in die Shell integrierter Befehl sein und hat keine Auswirkung (außer zu überprüfen, ob Sie in dieses Verzeichnis wechseln können und welche möglichen Nebenwirkungen dies für automountbare Verzeichnisse haben kann), wenn es sich um eine ausführbare Datei handelt. Deshalb funktioniert Ihr zweites Beispiel nicht.

mouviciel
quelle
4

Neben der bestehenden guten Antwort ist auch zu erwähnen, dass eine Pipe einen neuen Prozess auslöst, der über ein eigenes Arbeitsverzeichnis verfügt. Daher funktioniert dies nicht:

echo test | cd /

Daher befinden Sie sich nicht mehr im Ordner /, nachdem die Shell von diesem Befehl zurückgekehrt ist.

Mark Rejhon
quelle
Alle Befehle in einer Pipeline läuft in verschiedenen Prozessen, so in a | b, auch wenn aund bsind gebautet mindestens ein dann nicht in dem Shell - Prozess laufen, aber es gibt keine Garantie , die das es ist. Zum Beispiel in AT & T ksh, zshoder bash -O lastpipe, bwird in dem aktuellen Shell - Prozess ausgeführt, so dass Ihr Code , den Sie / es dauern würde.
Stéphane Chazelas
4

Zusätzlich zu den bereits gegebenen richtigen Antworten: Wenn Sie bash ausführen und herausfinden möchten , was ein "Befehl" wie cd ist , können Sie type verwenden

$ type cd
cd is a shell builtin

oder warum nicht:

$ type time
time is a shell keyword

während zum beispiel gnu time normalerweise schon in ihrer lieblingsdistribution enthalten ist:

$ which time
/usr/bin/time

Okey okey du hast die Idee, was zum Teufel ist dann Typ?

$ type type
type is a shell builtin

Hier ist ein Bash-Handbuch-Snippet:

       type [-aftpP] name [name ...]
          With no options, indicate how each name would be interpreted  if  used  as  a
          command name.  If the -t option is used, type prints a string which is one of
          alias, keyword, function, builtin,  or  file  if  name  is  an  alias,  shell
          reserved word, function, builtin, or disk file, respectively.  If the name is
          not found, then nothing is printed, and an exit status of false is  returned.
          If  the -p option is used, type either returns the name of the disk file that
          would be executed if name were specified as a command  name,  or  nothing  if
          ‘‘type  -t  name’’ would not return file.  The -P option forces a PATH search
          for each name, even if ‘‘type -t name’’ would not return file.  If a  command
          is  hashed,  -p  and -P print the hashed value, not necessarily the file that
          appears first in PATH.  If the -a option is used,  type  prints  all  of  the
          places  that  contain  an  executable  named name.  This includes aliases and
          functions, if and only if the -p option is  not  also  used.   The  table  of
          hashed  commands  is  not  consulted when using -a.  The -f option suppresses
          shell function lookup, as with the command builtin.  type returns true if any
          of the arguments are found, false if none are found.
3molo
quelle
0

Wie andere gesagt haben, wird es nicht funktionieren, da cdes sich um einen in die Shell eingebauten Befehl handelt, kein externes Programm, so dass es keine Standardeingabe gibt, in die Sie irgendetwas umleiten können.

Aber selbst wenn es funktionieren würde, würde es nicht das tun, was Sie wünschen: Eine Pipe erzeugt einen neuen Prozess und leitet die Standardausgabe des ersten Befehls in die Standardeingabe des zweiten um, sodass nur der neue Prozess seine aktuelle Arbeitsweise ändert Verzeichnis; Dies konnte den ersten Prozess in keiner Weise beeinflussen.

Massimo
quelle
2
Dein zweiter Absatz ist richtig. Aber noch einmal zum ersten Absatz: Warum gehen Sie davon aus, dass Shell-Builtins kein Standard haben? readist normalerweise (immer?) eine eingebaute Shell. Es ist wahr, dass cdstdin ignoriert, aber das liegt nicht daran, dass es ein Builtin ist.
dubiousjim
-2

Eine weitere Option sind Backticks, bei denen die Standardausgabe eines Befehls als Befehlszeilenargument eines zweiten Befehls verwendet wird. Sie sind portabler als $(...). Beispielsweise:

cd `echo $HOME`

oder allgemeiner;

cd `anycommand -and whatever args`

Beachten Sie, dass die Verwendung von Backticks von der Shell abhängt, die den Befehl ausführt und die Ausgabe in der Befehlszeile ersetzt. Die meisten Muscheln unterstützen es.

Seth Noble
quelle
3
Das OP hat bereits in seiner Frage festgestellt, dass dies $(...)funktioniert. Ich halte es nicht für ratsam, Backticks zu empfehlen, da diese viel komplizierter und fehleranfälliger sind. (Siehe §3.5.4 "Befehlsersetzung" im Bash-Referenzhandbuch .)
ruakh
$ () ist nett, wenn es unterstützt wird, aber Backticks werden auf verschiedenen Shells und Systemen häufiger unterstützt. Aber ich sollte dies als "eine andere Option" umformulieren.
Seth Noble
Verschiedene Muscheln, ja; aber andere "systeme"? Gibt es wirklich Shells, die $(...)auf einem System unterstützt werden, auf einem anderen jedoch nicht?
Ruakh
4
-1, es beantwortet die Frage überhaupt nicht.
Bernhard
3
@ruakh & Seth: Unterstützung aller POSIX-Shells $(…). Systeme ohne POSIX-Shell (dh mit einer echten Bourne-Shell) wären extrem alt. Sogar Systeme, bei denen /bin/shes sich um eine Bourne-Shell handelt und Sie einen anderen Pfad benötigen /usr/xpg4/bin/sh, um eine POSIX-Shell zu erhalten, sind heutzutage selten. Das Empfehlen von Backticks für alle, die alte Unix-Boxen nicht professionell verwalten, ist ein Nachteil.
Gilles 'SO- hör auf böse zu sein'