Verzeichnisse mit zwei oder mehr Dateien

11

Ich möchte ein Unterverzeichnis des aktuellen Verzeichnisses finden, das (dh das Unterverzeichnis) zwei oder mehr reguläre Dateien enthält.

Ich interessiere mich nicht für Verzeichnisse mit weniger als 2 Dateien, noch für Verzeichnisse, die nur Unterverzeichnisse enthalten.

porton
quelle

Antworten:

12

Hier ist ein völlig anderer Ansatz basierend auf GNU findund uniq. Dies ist viel schneller und viel CPU-freundlicher als Antworten, die auf der Ausführung eines Shell-Befehls basieren, der Dateien für jedes gefundene Verzeichnis zählt.

find . -type f -printf '%h\n' | sort | uniq -d

Der findBefehl druckt das Verzeichnis aller Dateien in der Hierarchie und uniqzeigt nur die Verzeichnisse an, die mindestens zweimal angezeigt werden.

xhienne
quelle
2
Sie sollten die Ausgabe von nicht analysieren find. In diesem Fall, weil GNU finddie Namen von Verzeichnissen mit Zeichen entstellt, die im aktuellen Gebietsschema nicht druckbar sind (wie "ä" im Gebietsschema C). Siehe auch unix.stackexchange.com/questions/321697/…
Kusalananda
4
@Kusalananda, nicht wenn die Ausgabe nicht zu einem tty geht. Hier ist das einzige Problem mit den Zeilenumbrüchen, die Sie mit-printf '%h\0' | sort -z | uniq -zd | xargs -r0 ...
Stéphane Chazelas
6
find . -type d \
    -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' \
    -print

Dadurch werden alle Namen im oder unter dem aktuellen Verzeichnis gefunden und anschließend alle Namen herausgefiltert, die keine Namen von Verzeichnissen sind.

Die verbleibenden Verzeichnisnamen werden diesem kurzen Skript zugewiesen:

c=0
for n in "$1"/*; do
    [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 ))
done

[ "$c" -ge 2 ]

Dieses Skript zählt die Anzahl der regulären Dateien (Überspringen symbolischer Links) in dem Verzeichnis, das als erstes Befehlszeilenargument (von find) angegeben wurde. Der letzte Befehl im Skript ist ein Test, um festzustellen, ob die Anzahl 2 oder höher war. Das Ergebnis dieses Tests ist der Rückgabewert (Exit-Status) des Skripts.

Wenn der Test erfolgreich war, -printwird findder Pfad zum Verzeichnis ausgedruckt.

Um auch versteckte Dateien (Dateien, deren Namen mit einem Punkt beginnen) zu berücksichtigen, ändern Sie das sh -cSkript von "sagen"

for n in "$1"/*; do

zu

for n in "$1"/* "$1"/.*; do

Testen:

$ tree
.
`-- test
    |-- a
    |-- dir1
    |   |-- a
    |   |-- b
    |   `-- c
    `-- dir2
        |-- dira
        |-- dirb
        |   |-- file-1
        |   `-- file-2
        `-- dirc

6 directories, 6 files

$ find . -type d -exec sh -c 'c=0; for n in "$1"/*; do [ -f "$n" ] && [ ! -h "$n" ] && c=$(( c + 1 )); done; [ "$c" -ge 2 ]' sh {} ';' -print
./test/dir1
./test/dir2/dirb
Kusalananda
quelle
Ihre Lösung zählt keine Dateien mit einem Namen, der mit einem Punkt beginnt. Sie sollten auch c = 0 initialisieren, um Fehlermeldungen mit Verzeichnissen zu vermeiden, die keine Datei enthalten.
Xhienne
@xhienne Ich habe versteckte Dateien in Betracht gezogen und werde einen Hinweis dazu hinzufügen. Es gibt keinen Fehler, wenn sich keine regulären Dateien in einem Verzeichnis befinden, da dies [ "" -ge 2 ]ein gültiger Test ist.
Kusalananda
Nicht sicher, wie Sie "gültig" definieren. Für POSIX muss arg1 ein ganzzahliger Wert sein. dash, bash --posixUnd testalle Display eine Fehlermeldung und Ausgang mit 2 (dh „Ein Fehler ist aufgetreten“)
xhienne
@ xhienne Ah, ich habe auf einem System getestet, kshdas als läuft sh. Wird sofort geändert. Danke, dass du mich angestupst hast! :-)
Kusalananda
Auch [ -f ... ]dereferenziert symbolische Links. Sie sollten einen Test hinzufügen, um sie zu beseitigen, da die Frage angibt, dass nur reguläre Dateien gezählt werden sollen.
Xhienne
6

Mit Hilfe von Gilles 'Antwort auf SU und seiner Umkehrung und einigen Modifikationen, hier was Sie brauchen.

find . -type d -exec sh -c 'set -- "$1"/*;X=0; 
    for args; do [ -f "$args" ] && X=$((X+1)) ;done; [ "$X" -gt 1 ] ' _ {} \; -print

Verzeichnisbaum.

.
├── test
│   ├── dir1
│   │   ├── a
│   │   ├── b
│   │   └── c
│   ├── dir2
│   │   ├── dira
│   │   │   └── a file\012with\012multiple\012line
│   │   ├── dirb
│   │   │   ├── file-1
│   │   │   └── file-2
│   │   └── dirc
│   ├── diraa
│   ├── dirbb
│   ├── dircc
│   └── x
│   └── x1
│   └── x2
└── test2
    ├── dir3
    └── dir4

Ergebnis:

./test
./test/dir1
./test/dir2/dirb
αғsнιη
quelle
Ich hatte dies zuerst auch, aber Sie werden Probleme mit Verzeichnissen haben, die mehrere Unterverzeichnisse und Dateien enthalten. Verzeichnisse, die nur Unterverzeichnisse enthalten, werden nicht ausgesondert.
Kusalananda
Es löst es nicht wirklich. Es findet sowohl das testals auch das dir2Verzeichnis in meinem Testaufbau (siehe meine Antwort).
Kusalananda
Funktioniert für Ihr Beispiel, aber fügen Sie test/x1und test/x2als Dateien hinzu ... $1und $2werden Verzeichnisse für test, und das Verzeichnis wird übersehen.
Kusalananda
@Kusalananda Ich habe keine Möglichkeit gefunden, außer dem, was Sie geantwortet haben. Ich habe versucht, einen Teil meines Befehls so zu ändern, dass er nicht genau Ihr Duplikat ist (ich habe versteckte Dateien nicht ausgeschlossen, wie Sie es getan haben). Ich entschuldige mich.
αғsнιη
1
Keine Sorge :-)
Kusalananda
3

Ein weiterer find+ wcAnsatz:

find path/currdir -maxdepth 1 -type d ! -empty ! -path "path/currdir" \
-exec sh -c 'count=$(find "$1" -maxdepth 1 -type f | wc -l); [ $count -ge 2 ]' _ {} \; -print

  • path/currdir - Pfad zu Ihrem aktuellen Verzeichnis

  • -maxdepth 1- Berücksichtigen Sie nur direkte untergeordnete Unterordner

  • ! -empty - leere Unterordner ignorieren

  • ! -path "path/currdir" - Ignorieren Sie den aktuellen Verzeichnispfad

  • count=$(find "$1" -maxdepth 1 -type f | wc -l)- countwird mit der Anzahl der Dateien für jeden gefundenen Unterordner zugewiesen

  • [ $count -ge 2 ] ... -print - Drucken Sie den Namen / Pfad des Unterordners, der 2 oder mehr reguläre Dateien enthält

RomanPerekhrest
quelle