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/CCCC
verwenden würde .find
/A/AA /A/AB/ABB /B /C/CC/CCC/CCCC
Der Ausgangspunkt wäre find . -type d
, aber weder -mindepth
noch -maxdepth
können verwendet werden. Kann -noleaf
helfen (ich konnte es nicht dazu bringen, die gewünschte Reaktion zu erzielen)?
Antwort1
Hier ist eine POSIX-kompatible Lösung, die die Ausgabe nachbearbeitet, find
um 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 find
Unterverzeichnisse unmittelbar nach ihrem übergeordneten Verzeichnis aufgelistet werden. Der zusätzliche Vorteil "/"
besteht darin, das fälschliche Entfernen zu vermeiden, foo
wenn foobar
auch vorhanden ist. Das Unelegante NR!=1
vermeidet 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 sub
entfernt 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 zshexpn
Manpage. 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; N
bedeutet, dass die Erweiterung leer ist, wenn keine Übereinstimmung vorliegt; D
bewirkt, dass Punktdateien eingeschlossen werden; :h
ist 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_leaf
Funktion:
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.