GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Symbolische Link-Rekursion – was macht sie „zurücksetzen“?

Ich habe ein kleines Bash-Skript geschrieben, um zu sehen, was passiert, wenn ich einem symbolischen Link folge, der auf dasselbe Verzeichnis verweist. Ich hatte erwartet, dass es entweder ein sehr langes Arbeitsverzeichnis erstellen oder abstürzen würde. Aber das Ergebnis hat mich überrascht…

mkdir a
cd a

ln -s ./. a

for i in `seq 1 1000`
do
  cd a
  pwd
done

Ein Teil der Ausgabe ist

${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
${HOME}/a
${HOME}/a/a
${HOME}/a/a/a
${HOME}/a/a/a/a
${HOME}/a/a/a/a/a
${HOME}/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a
${HOME}/a/a/a/a/a/a/a/a

was passiert hier?

Akzeptierte Antwort:

Patrice identifizierte die Ursache des Problems in seiner Antwort, aber wenn Sie wissen möchten, wie Sie von dort zu der Ursache gelangen, finden Sie 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, der ein Handle zu einer Datei des Typs Verzeichnis ist, wo relative Pfade (in Systemaufrufen, die durch den Prozess gemacht werden) beginnen. Beim Auflösen eines relativen Pfads muss der Kernel nicht den (a) vollständigen Pfad zu diesem aktuellen Verzeichnis kennen, er liest nur die Verzeichniseinträge in dieser Verzeichnisdatei, um die erste Komponente des relativen Pfads (und .. ist in dieser Hinsicht wie jede andere Datei) und wird von dort fortgesetzt.

Nun, als Benutzer möchten Sie manchmal wissen, wo sich dieses Verzeichnis im Verzeichnisbaum 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 (/ ) in eine beliebige Datei. Dieser Pfad wird allgemein als kanonischer Pfad bezeichnet.

Um den Pfad des aktuellen Arbeitsverzeichnisses zu erhalten, muss ein Prozess einfach nach oben (also runter) gehen wenn Sie einen Baum mit seiner Wurzel unten sehen möchten) den Baum zurück zur Wurzel, wobei Sie die Namen der Knoten auf dem Weg finden.

Zum Beispiel versucht ein Prozess herauszufinden, dass sein aktuelles Verzeichnis /a/b/c ist , würde den .. öffnen Verzeichnis (relativer Pfad, also .. ist der Eintrag im aktuellen Verzeichnis) und suchen Sie nach einer Datei vom Typ Verzeichnis mit der gleichen Inode-Nummer wie . , finden Sie heraus, dass c Übereinstimmungen, öffnet dann ../.. und so weiter, bis / gefunden wird . Da gibt es keine Zweideutigkeit.

Dafür sorgt getwd() oder getcwd() C-Funktionen tun es oder taten es zumindest früher.

Auf einigen Systemen wie modernem Linux gibt es einen Systemaufruf, um den kanonischen Pfad zum aktuellen Verzeichnis zurückzugeben, der diese Suche im Kernelbereich durchführt (und es Ihnen ermöglicht, Ihr aktuelles Verzeichnis zu finden, auch wenn Sie keinen Lesezugriff auf alle seine Komponenten haben). , und genau das ist getcwd() ruft dort an. Auf modernen Linux finden Sie den Pfad zum aktuellen Verzeichnis auch über einen readlink() auf /proc/self/cwd .

Das machen die meisten Sprachen und frühen Shells, wenn sie den Pfad zum aktuellen Verzeichnis zurückgeben.

In Ihrem Fall können Sie cd a aufrufen beliebig oft, denn es ist ein Symlink auf . , das aktuelle Verzeichnis ändert sich nicht, also alles von getcwd() , pwd -P , python -c 'import os; print os.getcwd()' , perl -MPOSIX -le 'print getcwd' würde Ihren ${HOME} zurückgeben .

Nun, Symlinks machten das alles noch komplizierter.

symlinks Sprünge im Verzeichnisbaum zulassen. Unter /a/b/c , falls /a oder /a/b oder /a/b/c ein Symlink ist, dann der kanonische Pfad von /a/b/c wäre was ganz anderes. Insbesondere die .. Eintrag in /a/b/c ist nicht unbedingt /a/b .

In der Bourne-Shell, wenn Sie dies tun:

cd /a/b/c
cd ..

Oder sogar:

cd /a/b/c/..

Es gibt keine Garantie, dass Sie in /a/b landen .

Genau wie:

vi /a/b/c/../d

ist nicht unbedingt dasselbe wie:

vi /a/b/d

ksh führte ein Konzept eines logischen aktuellen Arbeitsverzeichnisses ein um das irgendwie zu umgehen. Die Leute gewöhnten sich daran und schließlich spezifizierte POSIX dieses Verhalten, was bedeutet, dass die meisten Shells es heutzutage auch tun:

Verwandte:Linux – Verstehen Sie die Protokollierung in Linux?

Für die cd und pwd eingebaute Befehle (und nur für sie (allerdings auch für popd /pushd auf Shells, die sie haben)), behält die Shell ihre eigene Vorstellung vom aktuellen Arbeitsverzeichnis bei. Es wird im $PWD gespeichert spezielle Variable.

Wenn Sie dies tun:

cd c/d

auch wenn c oder c/d sind Symlinks, während $PWD enthält /a/b , es hängt c/d an bis zum Ende also $PWD wird zu /a/b/c/d . Und wenn Sie das tun:

cd ../e

Anstelle von chdir("../e") , es tut chdir("/a/b/c/e") .

Und das pwd Der Befehl gibt nur den Inhalt von $PWD zurück Variable.

Das ist in interaktiven Shells nützlich, weil pwd gibt einen Pfad zum aktuellen Verzeichnis aus, der Auskunft darüber gibt, wie Sie dorthin gelangt sind und solange Sie nur .. verwenden in Argumenten zu cd und nicht andere Befehle, ist es weniger wahrscheinlich, dass Sie überrascht werden, weil cd a; cd .. oder cd a/.. würde Sie im Allgemeinen dorthin zurückbringen, wo Sie waren.

Nun, $PWD wird nicht geändert, es sei denn, Sie machen ein cd . Bis zum nächsten Aufruf von cd oder pwd , viele Dinge könnten passieren, jede der Komponenten von $PWD umbenannt werden könnte. Das aktuelle Verzeichnis ändert sich nie (es ist immer derselbe Inode, obwohl er gelöscht werden könnte), aber sein Pfad im Verzeichnisbaum könnte sich vollständig ändern. getcwd() berechnet das aktuelle Verzeichnis jedes Mal, wenn es aufgerufen wird, indem es den Verzeichnisbaum hinuntergeht, sodass seine Informationen immer korrekt sind, aber für das logische Verzeichnis, das von POSIX-Shells implementiert wird, die Informationen in $PWD könnte alt werden. Also beim Ausführen von cd oder pwd , einige Muscheln möchten sich vielleicht davor schützen.

In diesem speziellen Fall sehen Sie unterschiedliche Verhaltensweisen mit unterschiedlichen Shells.

Manche mögen ksh93 Ignorieren Sie das Problem vollständig, so dass auch nach dem Aufruf von cd falsche Informationen zurückgegeben werden (und Sie würden nicht das Verhalten sehen, das Sie mit bash sehen dort).

Manche mögen bash oder zsh überprüfen Sie das $PWD ist immer noch ein Pfad zum aktuellen Verzeichnis auf cd , aber nicht auf pwd .

pdksh überprüft beide pwd und cd (aber nach pwd , aktualisiert $PWD nicht )

ash (zumindest die auf Debian gefundene) überprüft nicht, und wenn Sie cd a ausführen , es macht tatsächlich cd "$PWD/a" , also wenn sich das aktuelle Verzeichnis geändert hat und $PWD nicht mehr auf das aktuelle Verzeichnis zeigt, wird es tatsächlich nicht zu a wechseln Verzeichnis im aktuellen Verzeichnis, sondern das in $PWD (und einen Fehler zurückgeben, wenn er nicht vorhanden ist).

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 Muscheln.

In Ihrem Fall, da Sie bash verwenden , nach einem cd a , bash prüft, ob $PWD zeigt immer noch auf das aktuelle Verzeichnis. Dazu ruft es stat() auf auf den Wert von $PWD um seine Inode-Nummer zu prüfen und mit der von . zu vergleichen .

Aber beim Nachschlagen von $PWD path beinhaltet das Auflösen zu vieler Symlinks, die stat() kehrt mit einem Fehler zurück, sodass die Shell nicht prüfen kann, ob $PWD entspricht immer noch dem aktuellen Verzeichnis, also wird es mit getcwd() neu berechnet und aktualisiert $PWD entsprechend.

Um Patrices Antwort zu verdeutlichen:Diese Überprüfung der Anzahl der Symlinks, die beim Suchen eines Pfads angetroffen werden, dient dem Schutz vor Symlink-Schleifen. Die einfachste Schleife kann mit

erstellt werden
rm -f a b
ln -s a b
ln -s b a

Ohne diesen Schutz, bei einem cd a/x , müsste das System finden, wo a verlinkt auf, findet es b und ist ein Symlink, der auf a verweist , und das würde auf unbestimmte Zeit so weitergehen. Der einfachste Weg, sich davor zu schützen, besteht darin, aufzugeben, nachdem mehr als eine beliebige Anzahl von symbolischen Links aufgelöst wurde.

Verwandte Themen:Steam – Werden Mods auf mehrere Computer angewendet, wenn ich das Nexus-Konto mit dem Steam-Konto verknüpfe?

Nun zurück zum logischen aktuellen Arbeitsverzeichnis und warum es kein so gutes Feature ist. Es ist wichtig zu wissen, dass es nur für cd gilt in der Shell und nicht in anderen Befehlen.

Zum Beispiel:

cd -- "$dir" &&  vi -- "$file"

ist nicht immer dasselbe wie:

vi -- "$dir/$file"

Aus diesem Grund werden Sie manchmal feststellen, dass die Leute empfehlen, immer cd -P zu verwenden in Skripten, um Verwirrung zu vermeiden (Sie möchten nicht, dass Ihre Software ein Argument von ../x verarbeitet anders als andere Befehle, nur weil es in der Shell statt in einer anderen Sprache geschrieben ist).

Das -P Option ist, das logische Verzeichnis zu deaktivieren Handhabung so cd -P -- "$var" ruft tatsächlich chdir() auf auf den Inhalt von $var (mindestens solange $CDPATH es nicht gesetzt, und außer wenn $var ist - (oder möglicherweise -2 , +3 … in manchen Schalen) aber das ist eine andere Geschichte). Und nach einem cd -P , $PWD enthält einen kanonischen Pfad.


Linux
  1. Erstellen eines neuen Verzeichnisses in C

  2. So erstellen Sie einen Link zu einem Verzeichnis

  3. Node.js:Prüfen Sie, ob die Datei ein symbolischer Link ist, wenn Sie mit „fs“ über das Verzeichnis iterieren

  4. Warum erstellt mein symbolischer Link eine Datei und keinen Ordner?

  5. Verwendung von / bei Verwendung von CD

Was sind symbolische Links in Linux? Wie erstellt man symbolische Links?

Absoluter vs. relativer Pfad in Linux:Was ist der Unterschied?

Linux:Fügen Sie PATH ein Verzeichnis hinzu

Was passiert, wenn mv unterbrochen wird?

Was gibt pwd aus?

Was passiert, wenn ich lost+found lösche?