it-swarm.com.de

Suchen Sie nach Verzeichnissen, die KEINE Datei enthalten

Ja, ich sortiere meine Musik. Ich habe alles in dem folgenden Mantra wunderschön arrangiert: /Artist/Album/Track - Artist - Title.ext und wenn eines existiert, befindet sich das Cover in /Artist/Album/cover.(jpg|png).

Ich möchte alle Verzeichnisse der zweiten Ebene durchsuchen und diejenigen finden, die kein Cover haben. Mit dem zweiten Level meine ich, es ist mir egal, ob /Britney Spears/ keine cover.jpg hat, aber es wäre mir wichtig, wenn /Britney Spears/In The Zone/ keine hat.

Mach dir keine Sorgen über das Herunterladen des Covers (das ist ein lustiges Projekt für mich von morgen). Mir geht es nur um die glorreiche Schüchternheit eines inversen find Beispiels.

57
Oli

Einfach, es stellt sich heraus. Im Folgenden wird eine Liste der Verzeichnisse mit dem Cover abgerufen und diese mit einer Liste aller Verzeichnisse der zweiten Ebene verglichen. Zeilen, die in beiden "Dateien" vorkommen, werden unterdrückt und es verbleibt eine Liste der Verzeichnisse, für die Deckblätter erforderlich sind.

comm -3 \
    <(find ~/Music/ -iname 'cover.*' -printf '%h\n' | sort -u) \
    <(find ~/Music/ -maxdepth 2 -mindepth 2 -type d | sort) \
| sed 's/^.*Music\///'

Hurra.

Anmerkungen:

  • Die Argumente von comm lauten wie folgt:

    • -1 unterdrückt Zeilen, die für Datei1 eindeutig sind
    • -2 unterdrückt Zeilen, die nur für Datei2 gelten
    • -3 unterdrückt Zeilen, die in beiden Dateien erscheinen
  • comm akzeptiert nur Dateien, daher die Eingabemethode kooky <(...). Dies leitet den Inhalt über eine echte [temporäre] Datei weiter.

  • comm benötigt sortierte Eingaben oder es funktioniert nicht und find garantiert in keinem Fall eine Bestellung. Es muss auch einzigartig sein. Bei der ersten find Operation wurden möglicherweise mehrere Dateien für cover.* gefunden, sodass möglicherweise doppelte Einträge vorhanden sind. sort -u zerzaust diese schnell auf eins. Der zweite Fund wird immer einzigartig sein.

  • dirname ist ein praktisches Werkzeug, um das Verzeichnis einer Datei abzurufen, ohne auf sed (et al.) Zurückzugreifen.

  • find und comm sind beide etwas unordentlich in ihrer Ausgabe. Das letzte sed ist da, um die Dinge aufzuräumen, damit Sie mit Artist/Album übrig bleiben. Dies kann für Sie wünschenswert oder nicht wünschenswert sein.

11
Oli

Fall 1: Sie kennen den genauen Dateinamen, nach dem Sie suchen müssen

Verwenden Sie find mit test -e your_file, um zu überprüfen, ob eine Datei vorhanden ist. Sie suchen beispielsweise nach Verzeichnissen, in denen sich kein cover.jpg befindet:

find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec test -e "{}/cover.jpg" ';' -print

Es ist jedoch case sensitive.

Fall 2: Sie möchten flexibler sein

Sie sind sich des Falls nicht sicher, und die Erweiterung könnte jPg, png sein ...

find base_dir -mindepth 2 -maxdepth 2 -type d '!' -exec sh -c 'ls -1 "{}"|egrep -i -q "^cover\.(jpg|png)$"' ';' -print

Erläuterung:

  • Sie müssen für jedes Verzeichnis eine Shell sh erzeugen, da bei Verwendung von find kein Piping möglich ist.
  • ls -1 "{}" gibt nur die Dateinamen des Verzeichnisses aus, das find gerade durchläuft
  • egrep (anstelle von grep) verwendet erweiterte reguläre Ausdrücke. -i macht die Suche unabhängig von Groß- und Kleinschreibung, -q lässt die Ausgabe weg
  • "^cover\.(jpg|png)$" ist das Suchmuster. In diesem Beispiel stimmt es z.B. cOver.png, Cover.JPG oder cover.png. Der . muss mit einem Escapezeichen versehen werden, da er sonst mit any übereinstimmt. ^ markiert den Anfang der Zeile, $ das Ende

Andere Beispiele für Suchmuster für egrep :

Ersetzen Sie den Teil egrep -i -q "^cover\.(jpg|png)$" durch:

  • egrep -i -q "cover\.(jpg|png)$": Entspricht auch cd_cover.png, album_cover.JPG ...
  • egrep -q "^cover\.(jpg|png)$": Entspricht cover.png, cover.jpg, aber NICHT Cover.jpg (Groß-/Kleinschreibung wird nicht deaktiviert)
  • egrep -iq "^(cover|front)\.jpg$": entspricht z. front.jpg, Cover.JPG aber nicht ​​Cover.PNG

Weitere Informationen hierzu finden Sie unter Reguläre Ausdrücke .

80
phoibos

Dies ist mit Globbing viel besser zu lösen als mit find.

$ cd ... # to the directory one level above the album/artist structure

$ echo */*/*.cover   # lists all the covers

$ printf "%s\n" */*/*.cover # lists all the covers, one per line

Angenommen, Sie haben keine Streudateien in dieser Nice-Struktur. Das aktuelle Verzeichnis enthält nur Interpreten-Unterverzeichnisse und diese enthalten nur Album-Unterverzeichnisse. Dann können wir so etwas machen:

$ diff  <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)

Die Syntax <(...) ist die Bash-Prozessersetzung: Sie können einen Befehl anstelle eines Dateiarguments verwenden. Sie können die Ausgabe eines Befehls als Datei behandeln. Wir können also zwei Programme ausführen und deren Diff verwenden, ohne ihre Ausgabe in temporären Dateien zu speichern. Das Programm diff glaubt, dass es mit zwei Dateien arbeitet, aber tatsächlich liest es aus zwei Pipes.

Der Befehl, der die rechte Eingabe für diff, printf "%s\n" */* erzeugt, listet nur die Albumverzeichnisse auf. Der Befehl für die linke Hand durchläuft die Pfade *.cover und gibt deren Verzeichnisnamen aus.

Testlauf:

$ find .   # let's see what we have here
.
./a
./a/b
./foo
./foo/bar
./foo/baz
./foo/baz/cover.jpg

$ diff  <(for x in */*/cover.jpg; do echo "$(dirname "$x")" ; done) <(printf "%s\n" */*)
0a1,2
> a/b
> foo/bar

Aha, die Verzeichnisse a/b und foo/bar haben kein cover.jpg.

Es gibt einige Fälle mit unterbrochenen Ecken, so dass sich * standardmäßig zu sich selbst erweitert, wenn es zu nichts passt. Dies kann mit Bashs set -o nullglob behoben werden.

8
Anon
ls --color=never */*.txt | sed 's|/.*||' | sort -u -n > withtxt.txt
ls --color=never -d * | sort -u -n > all.txt
diff all.txt withtxt.txt

Zeigt alle Verzeichnisse an, in denen sich keine txt-Dateien befinden.

0