find . -type d
normalerweise wird angezeigt
./dir1
./dir1/dir2
./dir3
./dir3/dir4
./dir3/dir4/dir5
...
Ich möchte nur die
./dir1/dir2
./dir3/dir4/dir5
, ohne sein übergeordnetes ./dir1, …
Mit anderen Worten, ich möchte nur Verzeichnisse sehen, die keine Unterverzeichnisse enthalten.
Irgendeine Idee?
EDIT: Ich habe festgestellt, dass es -links 2
in einer normalen Linux-Umgebung funktioniert, z. B.
docker run -it --rm ubuntu:bionic find /etc -type d -links 2
funktioniert perfekt.
Wenn ich jedoch ein Verzeichnis (von MacOS oder Windows) in den Docker-Container einbinde, ändern sich die Dinge und es funktioniert nicht. Sie können Folgendes versuchen:
docker run -it --rm -v /etc:/etc_of_host ubuntu:bionic find /etc_of_host -type d -links 2
Antwort1
Mit zsh
:
has_subdirs() () (( $# )) ${1-$REPLY}/*(ND/Y1)
print -rC1 -- **/*(ND/^+has_subdirs)
Oder direkt ohne Vermittlerfunktion:
print -rC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])
Das ist:
fn() some-command
definiert einefn
Funktion mitsome-command
als Hauptteil wie in der Bourne-Shell und den meisten Bourne-ähnlichen Shells.() { code } args
eine anonyme Funktion, diecode
mitargs
als Positionsparameter ausgeführt wird.(( $# ))
hier ist der Körper dieser anonymen Funktion ein arithmetischer Ausdruck, der sich auflöst zuWAHRwenn$#
(die Anzahl der Argumente) ungleich Null ist.${param-default}
(aus der Bourne-Shell) wird zu erweitert,$param
wenn der Parameter gesetzt ist oderdefault
andernfalls. Hier mit${1-$REPLY}
ermöglichen wir den direkten Aufruf unserer Funktion (alshas_subdirs mydir
) oder als Globbing-Qualifizierungsfunktion wie unten.$dir/*(ND/Y1)
erweitert sich auf die Dateien vom Typ „Verzeichnis in“$dir
(/
), einschließlich der versteckten (D
otglob), und gibt keinen Fehler aus, wenn es keine Übereinstimmung gibt (N
ullglob). Aber mitY1
stoppen wir bei der ersten Übereinstimmung. Daher gibt die anonyme Funktion (und daherhas_subdirs
) „true“ zurück, wenn das Verzeichnis mindestens ein Unterverzeichnis enthält.print -rC1
druckt seine Argumenter
aw auf1
C
Spalte^+has_subdirs
beschränkt auf Dateien, für die diehas_subdirs
Funktion () nicht^
true zurückgibt.
Wenn Sie die Shell verwenden müssen bash
, tun Sie einfach Folgendes:
zsh << 'EOF'
print -rC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])
EOF
Oder um das Ergebnis in einem Bash-Array zu speichern (erfordert Bash 4.4+):
readarray -td '' array < <(zsh << 'EOF'
print -rNC1 -- **/*(ND/^e['()(($#)) $REPLY/*(ND/Y1)'])
EOF
)
(unter Verwendung von NUL-getrennten Datensätzen, um beliebige Pfade speichern zu können).
Wenn Sie nicht über zsh
, aber über verfügen perl
, können Sie Folgendes tun:
find . -depth -type d -print0 |
perl -l -0ne 'print if rindex $prev, "$_/", 0; $prev = $_'
Oder wenn Sie GNU haben awk
:
find . -depth -type d -print0 |
gawk -v 'RS=\0' 'index(prev, $0"/") != 1; {prev = $0}'
Diese haben find
die Verzeichnisse gedrucktTiefe zuerst(Blätter vor den Zweigen, auf denen sie sich befinden) und ruft perl
/ ab awk
, um die Datensätze auszudrucken, die nicht gefunden wurden, gefolgt von /
am Anfang des vorherigen Datensatzes.
Um die Dateien in einem Bash 4.4+-Array zu speichern, müssen Sie in der Ausgabe auf NUL-getrennte Datensätze umschalten, indem Sie das -l
Nach -0
-in- perl
oder Add- -v 'ORS=\0'
in-Element verschieben gawk
:
readarray -td array < <(
find . -depth -type d -print0 |
perl -0lne 'print if rindex $prev, "$_/", 0; $prev = $_'
)
readarray -td array < <(
find . -depth -type d -print0 |
gawk -v 'RS=\0' -v 'ORS=\0' 'index(prev, $0"/") != 1; {prev = $0}'
)
Auf einigen Systemen und Dateisystemen (wie etwa ext4
Dateisystemen auf Linux-basierten Systemen) können Sie sich möglicherweise darauf verlassen, dass Verzeichnisse mit Unterverzeichnissen eine Linkanzahl von mehr als 2 haben ( dir
und dir/.
für jedes Unterverzeichnis ein zusätzlicher Link dir/subdir/..
).
print -rC1 -- **/*(ND/l-3)
Oder verwenden Sie find
in einer beliebigen Shell:
find . -type d -links -3
Dies funktioniert beispielsweise nicht auf btrfs
(das in mehreren Linux-Distributionen das Standarddateisystem ersetzt hat ext4
), wo die Linkanzahl der Verzeichnisse immer eins beträgt. Daher würde ich dies nicht als allgemeine Lösung empfehlen.
Dasselbe gilt für Union-Dateisysteme, wie in Ihrem Fall, wie overlayfs
dort, wo ich einige findezusammengeführtVerzeichnisse haben eine Linkanzahl von 1, unabhängig davon, ob sie Unterverzeichnisse enthalten oder nicht.
Wo immer es jedoch verwendbar ist, hat es gegenüber Lösungen, die die Unterverzeichnisse manuell zählen, den Vorteil, dass Sie keinen Lesezugriff auf das Verzeichnis benötigen (nur Suchzugriff auf das übergeordnete Verzeichnis).
Antwort2
Was Sie suchen, ist:
find . -type d -links 2
Jedes Verzeichnis im Dateisystem hat mindestens zwei Hardlinks – das Verzeichnis selbst im übergeordneten Verzeichnis und den Eintrag „.“ Jeder Eintrag „..“ in den Unterverzeichnissen fügt einen neuen Hardlink zum Verzeichnis hinzu. Sie müssen also nur Verzeichnisse mit zwei Hardlinks finden.
Der in einer anderen Antwort vorgeschlagene Befehl
find . -type d -links -3
Tut dasselbe, aber mit der Anweisung „Verzeichnisse mit weniger als drei Hardlinks dazu.“
Antwort3
Die Sortiertesten:
p="";(find . -type d;echo) | while read d; do [[ $p && $d != $p/* ]] && echo $p; p=$d; done
In einer Zeile
a="";(find . -type d;echo )|while read i; do if [[ (! $i =~ $a) && ("$a" != "") ]]; then echo $a; fi; a=$i;done
Wie im OP erwähnt:
a="";(find . -type d;echo )|while read i; do [[ $i != $a/* && ! -z "$a" ]] && echo $a; a=$i;done
Antwort4
Lesen man find
. Die -mindepth
Option ist die gewünschte.