Verwenden von „find“, um nur Verzeichnisse ohne weitere untergeordnete Verzeichnisse aufzulisten

Verwenden von „find“, um nur Verzeichnisse ohne weitere untergeordnete Verzeichnisse aufzulisten

Wie können nur Verzeichnisse aufgelistet werden, die kein weiteres Unterverzeichnis haben?

Stellen Sie sich eine Struktur vor, wie ich sie gerne nur zum Auflisten /A /A/AA /A/AB /A/AB/ABB /B /C /C/CC /C/CC/CCC /C/CC/CCC/CCCCverwenden würde .find/A/AA /A/AB/ABB /B /C/CC/CCC/CCCC

Der Ausgangspunkt wäre find . -type d, aber weder -mindepthnoch -maxdepthkönnen verwendet werden. Kann -noleafhelfen (ich konnte es nicht dazu bringen, die gewünschte Reaktion zu erzielen)?

Antwort1

Hier ist eine POSIX-kompatible Lösung, die die Ausgabe nachbearbeitet, findum Verzeichnisse zu entfernen, die ein aufgelistetes Unterverzeichnis haben. Dabei wird davon ausgegangen, dass die Verzeichnisnamen keine Zeilenumbrüche enthalten.

{ find . -type d; echo; } |
awk 'index($0,prev"/")!=1 && NR!=1 {print prev}
     1 {sub(/\/$/,""); prev=$0}'

Erklärung: Das awk-Skript verzögert das Drucken jeder Zeile, bis die nächste Zeile gelesen wurde, und druckt die vorherige Zeile nur, wenn es sich nicht um ein Präfix handelt. Dies nutzt die Tatsache aus, dass findUnterverzeichnisse unmittelbar nach ihrem übergeordneten Verzeichnis aufgelistet werden. Der zusätzliche Vorteil "/"besteht darin, das fälschliche Entfernen zu vermeiden, foowenn foobarauch vorhanden ist. Das Unelegante NR!=1vermeidet das Drucken einer anfänglichen leeren Zeile, und das Unelegante echo;besteht darin, keinen ebenso uneleganten Sonderfall für die letzte Zeile zu haben. Der Aufruf von subentfernt einen abschließenden Schrägstrich aus dem obersten Verzeichnis, falls eg find ./aufgerufen wurde.


Wie üblich gibt es einen kryptischen Zsh-Einzeiler.

echo **/.(e\''test -z $REPLY/*(/DN[1])'\':h)

Längere, besser lesbare Version:

is_leaf () { [ -z $REPLY/*(/DN[1]) ] }
echo **/.(+is_leaf:h)

Die letzte Zeile kann vereinfacht werden, echo **/(+is_leaf)wenn Sie das am Ende stehende nicht stört /.

Zusammenfassende Erklärung: Die Angaben in Klammern sindGlob-Qualifikation, dokumentiert in der zshexpnManpage. Wir filtern die Ergebnisse des Globs **/(erweitern auf das aktuelle Verzeichnis und alle seine Unterverzeichnisse) und behalten nur diejenigen bei, für die die Funktion is_leaf(oder der Code zwischen '…') 0 zurückgibt. Der Filtercode globpt die Unterverzeichnisse der getesteten Übereinstimmung ( $REPLY) (tatsächlich [1]stoppt er nach dem ersten Unterverzeichnis) und gibt einen Status zurück, der angibt, ob mindestens ein Unterverzeichnis gefunden wurde. Der Glob-Qualifizierer /beschränkt die Erweiterung auf Verzeichnisse; Nbedeutet, dass die Erweiterung leer ist, wenn keine Übereinstimmung vorliegt; Dbewirkt, dass Punktdateien eingeschlossen werden; :hist ein Verlaufsmodifikator und bewirkt, dass das /.Suffix entfernt wird (im Allgemeinen bedeutet es dirname).

Um die Möglichkeiten der Glob-Qualifizierer von zsh zu veranschaulichen, hier zwei weitere Varianten (länger und meiner Meinung nach obskurer) mit einer entsprechenden is_leafFunktion:

echo **/.(e\''tmp=($REPLY/*(/DN[1])); ((!#tmp))'\':h)
echo **/.(e\''$REPLY/*(/DN[1]e:REPLY=false:)'\':h)
is_leaf () { set -- $REPLY/*(/DN[1]); ((!#)); }
is_leaf () { return $REPLY/*(/DN[1]e:REPLY=1:) }

Antwort2

Das ist, was ich verwende:

leaf () { find "${1:-.}" -depth -type d | sed  'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D'; }

Rufen Sie es unter Verwendung des Startverzeichnisses auf:

leaf /start/dir

Antwort3

So suchen Sie nach Verzeichnissen ohne Unterverzeichnisse:

find dir -type d -links 2

Erklärung: Ein Verzeichnis besitzt einen Link zu jedem Unterverzeichnis darin, einen Link von seinem übergeordneten Verzeichnis und einen Link zu sich selbst. Wenn es keine Unterverzeichnisse besitzt, ergibt dies also 2 Links.

verwandte Informationen