Ich verstehe, dass ls -R
eine Liste von Verzeichnissen anzeigt. Aber warum ist es rekursiv? Wie wird dabei die Rekursion eingesetzt?
command-line
ls
Mint.K
quelle
quelle
ls
ein Verzeichnis gefunden wird, dieses Verzeichnis rekursiv aufgelistet wird.Antworten:
Definieren wir zunächst eine beliebige Ordnerstruktur:
In diesem
ls
Fall erhalten wir nur die Ausgabe des Basisordners:Wenn wir jedoch anrufen
ls -R
, bekommen wir etwas anderes:Wie Sie sehen können, wird es
ls
auf dem Basisordner und dann auf allen untergeordneten Ordnern ausgeführt. Und alle Enkelordner, unendlich. Tatsächlich durchläuft der Befehl jeden Ordner rekursiv, bis er das Ende der Verzeichnisstruktur erreicht. Zu diesem Zeitpunkt wird ein Zweig in der Struktur wiederhergestellt und das Gleiche für alle Unterordner, sofern vorhanden.Oder in Pseudocode:
Und weil ich kann, eine Referenz Java-Implementierung des gleichen.
quelle
Tatsächlich gibt es zwei eng gekoppelte Fragen, die Sie möglicherweise stellen.
ls
? Nach Ihrer Formulierung ("Wie wird die Rekursion in diesem Prozess verwendet?") Ist dies ein Teil dessen, was Sie wissen möchten. Diese Antwort beantwortet diese Frage.Warum es sinnvoll
ls
ist, mit einer rekursiven Technik implementiert zu werden:FOLDOC definiert Rekursion als:
Die natürliche Art der Implementierung
ls
besteht darin, eine Funktion zu schreiben, die eine Liste der anzuzeigenden Dateisystemeinträge und anderen Code erstellt, um Pfad- und Optionsargumente zu verarbeiten und die Einträge wie gewünscht anzuzeigen. Diese Funktion wird höchstwahrscheinlich rekursiv implementiert.ls
Bestimmt während der Optionsverarbeitung, ob eine rekursive Operation angefordert wurde (durch Aufrufen mit dem-R
Flag). In diesem Fall ruft sich die Funktion, die eine Liste der anzuzeigenden Einträge erstellt, einmal für jedes aufgeführte Verzeichnis auf, mit Ausnahme von.
und..
. Es kann separate rekursive und nicht rekursive Versionen dieser Funktion geben, oder die Funktion überprüft jedes Mal, ob sie rekursiv arbeiten soll.Ubuntu's
/bin/ls
, die ausführbare Datei, die beim Ausführen ausgeführt wirdls
, wird von GNU Coreutils bereitgestellt und verfügt über viele Funktionen. Infolgedessen ist der Code etwas länger und komplizierter als erwartet. Ubuntu enthält jedoch auch eine einfachere Versionls
von BusyBox . Sie können dies durch Eingabe ausführenbusybox ls
.Wie
busybox ls
benutzt man Rekursion:ls
in BusyBox ist implementiert incoreutils/ls.c
. Es enthält einescan_and_display_dirs_recur()
Funktion, die aufgerufen wird, um einen Verzeichnisbaum rekursiv zu drucken:Die Zeile, in der der rekursive Funktionsaufruf stattfindet, lautet:
Anzeigen der rekursiven Funktionsaufrufe, wenn sie auftreten:
Sie können dies in Betrieb sehen, wenn Sie
busybox ls
in einem Debugger ausgeführt werden. Installieren Sie zuerst die Debugsymbole, indem Sie die Pakete -dbgsym.ddeb aktivieren und dann dasbusybox-static-dbgsym
Paket installieren . Installieren Siegdb
auch (das ist der Debugger).Ich empfehle das Debuggen
coreutils ls
in einem einfachen Verzeichnisbaum.Wenn Sie keinen zur Hand haben, erstellen Sie einen (dies funktioniert genauso wie der
mkdir -p
Befehl in der Antwort von WinEunuuchs2Unix ):Und füllen Sie es mit einigen Dateien:
Sie können überprüfen, ob die
busybox ls -R foo
Arbeit wie erwartet ausgeführt wird, und die folgende Ausgabe erstellen:busybox
Im Debugger öffnen :GDB gibt einige Informationen über sich selbst aus. Dann sollte es ungefähr so lauten:
(gdb)
ist Ihre Eingabeaufforderung im Debugger. Das Erste, was Sie GDB an dieser Eingabeaufforderung mitteilen, ist das Festlegen eines Haltepunkts beim Start derscan_and_display_dirs_recur()
Funktion:Wenn Sie das ausführen, sollte Ihnen GDB Folgendes mitteilen:
Nun teilen Sie GDB mit, dass es
busybox
mit den folgenden Argumenten ausgeführt werden soll (oder mit welchem Verzeichnisnamen auch immer):ls -R foo
Möglicherweise sehen Sie Folgendes:
Wenn Sie
No such file or directory
wie oben sehen, ist das in Ordnung. Der Zweck dieser Demonstration ist nur zu sehen, wann diescan_and_display_dirs_recur()
Funktion aufgerufen wurde, sodass GDB den tatsächlichen Quellcode nicht untersuchen muss.Beachten Sie, dass der Debugger den Haltepunkt erreicht hat, noch bevor Verzeichniseinträge gedruckt wurden. Es bricht beim Zugriff auf diese Funktion ab, aber der Code in dieser Funktion muss ausgeführt werden, damit Verzeichnisse zum Drucken aufgelistet werden.
Führen Sie Folgendes aus, um GDB anzuweisen, fortzufahren:
Bei jedem
scan_and_display_dirs_recur()
Aufruf wird der Haltepunkt erneut erreicht, sodass Sie die Rekursion in Aktion sehen können. Es sieht so aus (einschließlich der(gdb)
Eingabeaufforderung und Ihrer Befehle):Die Funktion hat
recur
in ihrem Namen ... verwendet BusyBox sie nur, wenn das-R
Flag gegeben ist? Im Debugger ist dies leicht herauszufinden:Auch ohne
-R
diese spezielle Implementierung vonls
wird dieselbe Funktion verwendet, um herauszufinden, welche Dateisystemeinträge vorhanden sind, und um sie anzuzeigen.Wenn Sie den Debugger beenden möchten, sagen Sie es einfach:
Woher
scan_and_display_dirs_recur()
weiß, ob es sich selbst nennen sollte:Insbesondere wie funktioniert es anders, wenn das
-R
Flag übergeben wird? Das Untersuchen des Quellcodes (der möglicherweise nicht die genaue Version auf Ihrem Ubuntu-System ist) zeigt, dass er die interne Datenstruktur überprüftG.all_fmt
und dort speichert, mit welchen Optionen er aufgerufen wurde:(Wenn BusyBox ohne Unterstützung für kompiliert wurde
-R
, wird auch nicht versucht, Dateisystemeinträge rekursiv anzuzeigen. Darum geht es in diesemENABLE_FEATURE_LS_RECURSIVE
Teil.)Nur wenn
G.all_fmt & DISP_RECURSIVE
true ist, wird der Code ausgeführt, der den rekursiven Funktionsaufruf enthält.Andernfalls wird die Funktion nur einmal ausgeführt (pro in der Befehlszeile angegebenem Verzeichnis).
quelle
Wenn Sie darüber nachdenken, macht "rekursiv" Sinn für Befehle, die auf Verzeichnisse und deren Dateien und Verzeichnisse und deren Dateien und Verzeichnisse und deren Dateien und Verzeichnisse und deren Dateien wirken.
.... bis der gesamte Baum ab dem angegebenen Punkt vom Befehl bearbeitet wurde. In diesem Fall wird der Inhalt aller Unterverzeichnisse aller Unterverzeichnisse aller Unterverzeichnisse aufgeführt, die unter dem Befehl existieren Argument (e) des Befehls
quelle
-R steht für eine Rekursion, die locker "wiederholt" genannt werden könnte.
Nehmen Sie diesen Code zum Beispiel:
Das
-p
Erstellen von Verzeichnissen ermöglicht es Ihnen, Verzeichnisse mit einem einzigen Befehl zu erstellen. Wenn eines oder mehrere der Top-Middle-Verzeichnisse bereits vorhanden sind, liegt kein Fehler vor und die Middle-Lower-Verzeichnisse werden erstellt.Dann
ls -R
listet das rekursiv jedes einzelne Verzeichnis auf, beginnend mit temp, und arbeitet es sich durch den Baum bis zu allen Zweigen.Betrachten wir nun eine Ergänzung zum
ls -R
Befehl, dh dentree
Befehl:Wie Sie sehen können,
tree
leistet das Gleiche, alsls -R
sei es prägnanter und wage ich zu sagen "hübscher".Schauen wir uns nun an, wie Sie die soeben erstellten Verzeichnisse mit einem einfachen Befehl rekursiv entfernen:
Dadurch werden rekursiv
temp
alle darunter liegenden Unterverzeichnisse entfernt . dhtemp/a
,temp/b/1
undtemp/c/1/2
zuzüglich der Mitte Verzeichnisse dazwischen.quelle
tree
obwohl. Es ist ein großartiges Werkzeug.Hier ist eine einfache Erklärung, die sinnvoll ist, da bei der Anzeige des Inhalts von Unterverzeichnissen dieselbe Funktion bereits weiß, was mit einem Verzeichnis zu tun ist. Daher muss es sich nur in jedem Unterverzeichnis selbst aufrufen, um dieses Ergebnis zu erhalten!
Im Pseudocode sieht es ungefähr so aus:
quelle