Patrice identifizierte die Ursache des Problems in seiner Antwort , aber wenn Sie wissen möchten, wie Sie von dort zu dem Grund kommen, warum Sie das bekommen, ist hier die lange Geschichte.
Das aktuelle Arbeitsverzeichnis eines Prozesses ist nichts, was Sie für zu kompliziert halten würden. Es ist ein Attribut des Prozesses, das ein Handle für eine Datei vom Typ Verzeichnis ist, von dem aus relative Pfade (in vom Prozess ausgeführten Systemaufrufen) beginnen. Wenn ein relativer Pfad aufgelöst wird, muss der Kernel nicht den (a) vollständigen Pfad zu diesem aktuellen Verzeichnis kennen, sondern liest nur die Verzeichniseinträge in dieser Verzeichnisdatei, um die erste Komponente des relativen Pfads zu finden (und ..
verhält sich wie jede andere Datei in dieser Hinsicht) und fährt von dort fort.
Als Benutzer möchten Sie jetzt manchmal wissen, wo sich dieses Verzeichnis in der Verzeichnisstruktur befindet. Bei den meisten Unices ist der Verzeichnisbaum ein Baum ohne Schleife. Das heißt, es gibt nur einen Pfad von der Wurzel des Baums ( /
) zu einer bestimmten Datei. Dieser Weg wird allgemein als kanonischer Weg bezeichnet.
Um den Pfad des aktuellen Arbeitsverzeichnisses zu ermitteln, muss ein Prozess nur den Baum nach oben (und nach unten, wenn Sie einen Baum mit seiner Wurzel unten sehen möchten) zurück zur Wurzel gehen und die Namen der Knoten finden unterwegs.
Zum Beispiel würde ein Prozess, der versucht herauszufinden, dass es sich um sein aktuelles Verzeichnis handelt /a/b/c
, das ..
Verzeichnis öffnen (relativer Pfad, ebenso ..
wie der Eintrag im aktuellen Verzeichnis) und nach einer Datei vom Typ Verzeichnis mit der gleichen Inode-Nummer wie .
suchen c
passt, öffnet sich dann ../..
und so weiter, bis es findet /
. Da gibt es keine Mehrdeutigkeit.
Das ist, was die getwd()
oder getcwd()
C-Funktionen tun oder zumindest verwendet haben.
Auf einigen Systemen wie dem modernen Linux gibt es einen Systemaufruf, der den kanonischen Pfad zum aktuellen Verzeichnis zurückgibt, der die Suche im Kernelraum durchführt (und es Ihnen ermöglicht, Ihr aktuelles Verzeichnis zu finden, auch wenn Sie nicht auf alle seine Komponenten lesend zugreifen können). , und das getcwd()
nennt man dort. Unter modernen Linux finden Sie den Pfad zum aktuellen Verzeichnis auch über einen readlink () auf /proc/self/cwd
.
Das ist es, was die meisten Sprachen und frühen Shells tun, wenn sie den Pfad zum aktuellen Verzeichnis zurückgeben.
In Ihrem Fall können Sie anrufen , cd a
wie kann mal , wie Sie wollen, denn es ist ein symbolischer Link ist zu .
, wird das aktuelle Verzeichnis nicht so von allen ändern getcwd()
, pwd -P
, python -c 'import os; print os.getcwd()'
, perl -MPOSIX -le 'print getcwd'
zurückkehren würde Ihre ${HOME}
.
Nun erschwerten Symlinks all das.
symlinks
Erlaube Sprünge im Verzeichnisbaum. In /a/b/c
, wenn /a
oder /a/b
oder /a/b/c
ein symbolischer Link, dann die kanonische Pfad /a/b/c
etwas ganz anderes sein würde. Insbesondere ist der ..
Eintrag in /a/b/c
nicht zwingend /a/b
.
Wenn Sie in der Bourne-Shell Folgendes tun:
cd /a/b/c
cd ..
Oder auch:
cd /a/b/c/..
Es gibt keine Garantie dafür, dass Sie ankommen /a/b
.
So wie:
vi /a/b/c/../d
ist nicht unbedingt dasselbe wie:
vi /a/b/d
ksh
führte das Konzept eines logischen aktuellen Arbeitsverzeichnisses ein , um das irgendwie zu umgehen. Die Leute haben sich daran gewöhnt und POSIX hat letztendlich dieses Verhalten spezifiziert, was bedeutet, dass die meisten Shells es heutzutage auch tun:
Für die cd
und pwd
eingebauten Befehle ( und nur für sie (obwohl auch für popd
/ pushd
auf Shells, die sie haben)) behält die Shell ihre eigene Vorstellung des aktuellen Arbeitsverzeichnisses bei. Es ist in der $PWD
speziellen Variablen gespeichert .
Wenn Sie das tun:
cd c/d
auch wenn symlinks sind c
oder enthalten , so wird es an das ende angehängt . Und wenn du das tust:c/d
$PWD
/a/b
c/d
$PWD
/a/b/c/d
cd ../e
Anstatt es zu tun chdir("../e")
, tut es es chdir("/a/b/c/e")
.
Und der pwd
Befehl gibt nur den Inhalt der $PWD
Variablen zurück.
Dies ist in interaktiven Shells nützlich, da pwd
ein Pfad zum aktuellen Verzeichnis ausgegeben wird, der Informationen darüber enthält, wie Sie dorthin gelangt sind. Solange Sie nur ..
Argumente für cd
und keine anderen Befehle verwenden, ist es weniger wahrscheinlich, dass Sie überrascht werden, da Sie im Allgemeinen zurückkehren cd a; cd ..
oder cd a/..
dies tun würden zu wo du warst.
Wird jetzt $PWD
erst geändert, wenn Sie eine cd
. Bis Sie das nächste Mal anrufen cd
oder pwd
eine Menge Dinge passieren könnten, könnten alle Komponenten von $PWD
umbenannt werden. Das aktuelle Verzeichnis ändert sich nie (es ist immer derselbe Inode, obwohl es gelöscht werden könnte), aber sein Pfad in der Verzeichnisstruktur kann sich vollständig ändern. getcwd()
Berechnet das aktuelle Verzeichnis bei jedem Aufruf durch Durchlaufen der Verzeichnisstruktur, damit die Informationen immer korrekt sind. Bei dem von POSIX-Shells implementierten logischen Verzeichnis können die Informationen jedoch $PWD
veraltet sein. Wenn Sie also laufen cd
oder pwd
, möchten einige Muscheln möglicherweise dagegen schützen.
In diesem speziellen Fall sehen Sie unterschiedliche Verhaltensweisen mit unterschiedlichen Muscheln.
Manche ksh93
ignorieren das Problem vollständig und geben auch nach Ihrem Anruf falsche Informationen zurück cd
(und Sie würden das Verhalten, das Sie bash
dort beobachten, nicht sehen ).
Manche mögen bash
oder zsh
überprüfen, ob dies $PWD
noch ein Pfad zum aktuellen Verzeichnis ist cd
, aber nicht pwd
.
pdksh prüft beide pwd
und cd
(aber pwd
aktualisiert nicht $PWD
)
ash
(zumindest die, die auf Debian gefunden) überprüft nicht, und wenn Sie das tun cd a
, tut es tatsächlich cd "$PWD/a"
, so dass , wenn das aktuelle Verzeichnis geändert hat und $PWD
nicht mehr auf dem aktuellen Verzeichnis, es wird sich ändern , eigentlich nicht auf das a
Verzeichnis im aktuellen Verzeichnis , aber die in $PWD
(und einen Fehler zurück, wenn es nicht existiert).
Wenn Sie damit spielen möchten, können Sie Folgendes tun:
cd
mkdir -p a/b
cd a
pwd
mv ~/a ~/b
pwd
echo "$PWD"
cd b
pwd; echo "$PWD"; pwd -P # (and notice the bug in ksh93)
in verschiedenen Schalen.
In Ihrem Fall, da Sie bash
nach a verwenden cd a
, wird bash
überprüft, dass $PWD
immer noch auf das aktuelle Verzeichnis zeigt. Dazu ruft es stat()
den Wert von $PWD
auf, um seine Inode-Nummer zu überprüfen und mit der von zu vergleichen .
.
Wenn jedoch beim Aufrufen des $PWD
Pfads zu viele Symlinks aufgelöst werden, wird stat()
ein Fehler zurückgegeben, sodass die Shell nicht prüfen kann, ob sie $PWD
noch dem aktuellen Verzeichnis entspricht, und sie daher erneut mit berechnet getcwd()
und entsprechend aktualisiert $PWD
.
Um die Antwort von Patrice zu verdeutlichen, dient diese Überprüfung der Anzahl der beim Nachschlagen eines Pfads aufgetretenen Symlinks zum Schutz vor Symlink-Schleifen. Die einfachste Schleife kann mit gemacht werden
rm -f a b
ln -s a b
ln -s b a
Ohne diesen sicheren Schutz cd a/x
müsste das System auf einen Fall herausfinden, wohin a
Links führen, wo es sich befindet, b
und es ist ein Symlink, auf das Links führen a
, und das würde auf unbestimmte Zeit weitergehen. Die einfachste Möglichkeit, sich dagegen zu schützen, besteht darin, nach dem Auflösen von mehr als einer willkürlichen Anzahl von Symlinks aufzugeben.
Nun zurück zum logischen aktuellen Arbeitsverzeichnis und warum es nicht so gut ist. Es ist wichtig zu wissen, dass dies nur für cd
die Shell und nicht für andere Befehle gilt.
Zum Beispiel:
cd -- "$dir" && vi -- "$file"
ist nicht immer dasselbe wie:
vi -- "$dir/$file"
Aus diesem Grund wird manchmal empfohlen, immer cd -P
Skripte zu verwenden, um Verwirrung zu vermeiden (Sie möchten nicht, dass Ihre Software ein Argument behandelt, das sich ../x
von anderen Befehlen unterscheidet, nur weil es in Shell statt in einer anderen Sprache geschrieben ist).
Die -P
Option besteht darin, die Verarbeitung logischer Verzeichnisse zu deaktivieren , damit cd -P -- "$var"
tatsächlich chdir()
der Inhalt von aufgerufen wird $var
(außer wenn dies der Fall $var
ist, -
aber das ist eine andere Geschichte). Und nach ein cd -P
, $PWD
wird einen kanonischen Pfad enthält.