Wie kann ich Ordner finden, die x Dateien eines bestimmten Typs enthalten, und diese Pfade unter OSX ausgeben?

Wie kann ich Ordner finden, die x Dateien eines bestimmten Typs enthalten, und diese Pfade unter OSX ausgeben?

Ich habe dieses Skript für OSX, um Ordner zu finden, die nur eine Datei enthalten, und wenn es sich bei dieser Datei um eine Audiodatei handelt, wird der Pfad der Audiodatei ausgegeben

find "$1" -type d -exec sh -c '[[ $(find "$0" -mindepth 1 | wc -l) -eq 1 ]] 
&& [[ $(find "$0" -mindepth 1 -type d | wc -l) -eq 0 ]]  
&& find "$0"' {} \; |egrep ".mp4|.mp3|.ogg|.flac|.wma|.m4a"

d. h. verwenden Sie wie

./findodd.sh /Users/paul/Music

aber ich brauche zwei Verbesserungen:

  1. Was kann ich ändern, damit Dateien in Ordnern aufgelistet werden, die 2 Dateien, 3 Dateien usw. enthalten? Noch besser wäre es, wenn dies als Parameter übergeben werden könnte

  2. Derzeit findet es Ordner, die nur eine Datei enthalten, und diese Datei muss eine Audiodatei sein. Aber was ich wirklich möchte, ist, dass es Ordner findet, die nur eine Audiodatei enthalten, d. h. wenn der Ordner drei Dateien enthält, aber nur eine eine Audiodatei ist, möchte ich, dass diese Audiodatei aufgelistet wird.

Danke Paul

Antwort1

$ find
.
./folder3
./folder3/quux.txt
./folder1
./folder1/test.mp3
./folder1/test.txt
./folder1/test.wma
./folder2
./folder2/bar.txt
./folder2/foo.txt
./folder2/test.ogg

Beispielläufe:

$ ./findaudio.sh /tmp/findaudio 1
/tmp/findaudio/folder2/test.ogg

$ ./findaudio.sh /tmp/findaudio 2
/tmp/findaudio/folder1/test.mp3
/tmp/findaudio/folder1/test.wma

# The first parameter defaults to the current directory and
# the second parameter defaults to 1 so this works as well:
$ ./findaudio.sh
./folder2/test.ogg

Und hier der Code:

#!/bin/bash

shopt -s nullglob

find "${1:-.}" -type d | while read dir; do
        files=( "${dir}"/*.{mp4,mp3,ogg,flac,wma,m4a} )
        IFS=$'\n'
        (( ${#files[@]} == ${2:-1} )) && echo "${files[*]}"
done

Es durchläuft alle Unterverzeichnisse des angegebenen Verzeichnisses und verwendet Globbing, um alle Audiodateinamen des aktuellen Unterverzeichnisses in das Array einzulesen files. Wenn die Größe des Arrays Ihrem gewünschten Wert entspricht, werden die Dateinamen einfach durch eine neue Zeile getrennt ausgegeben.

BEARBEITEN: Dies ist mein früherer Ansatz, der auf der Annahme basiert, dass Sie die Ordner drucken möchten, nicht die betreffenden Dateinamen. Ich lasse es hier für zukünftige Referenzen.

$ find . \( -name '*.ogg' -o -name '*.wma' -o -name '*.mp3' \) -printf "%h\n" | uniq -u
./folder2

Dies findet alle Dateien mit den aufgelisteten Audioerweiterungen und druckt nur deren Verzeichniskomponenten statt des vollständigen Pfads. Dadurch erhalten Sie eine Liste der übergeordneten Ordner für alle Audiodateien. uniqNicht eindeutige Zeilen werden übersprungen, was Ihnen das gewünschte Ergebnis liefern sollte, d. h. es werden nur Ordner gedruckt, die genau eine Audiodatei enthalten.

Theoretisch sollte dies auch um einiges schneller sein als Ihr früherer Versuch.

Sie können dies verbessern, um Ihren ersten Punkt zu erfüllen, indem Sie die doppelten Zeilen zählen und nur die Ordner drucken, die Ihrer angeforderten Anzahl entsprechen. Eine naive Lösung wäre:

$ find . \( -name '*.ogg' -o -name '*.wma' -o -name '*.mp3' \) -printf "%h\n" | uniq -c | awk -v count=1 '$1==count'
1 ./folder2

$ find . \( -name '*.ogg' -o -name '*.wma' -o -name '*.mp3' \) -printf "%h\n" | uniq -c | awk -v count=2 '$1==count'
2 ./folder1

uniqAllerdings wäre es vielleicht besser, den -Teil und die rechte Seite des Rohrs in einer einzigen Linie zu verschmelzen awk.

Antwort2

ZWEITER VERSUCH

OK, nachdem ich dies selbst in meinem eigenen Musikordner ausprobiert habe, ist dies die Lösung für beide Ihrer Anfragen:

COMMAND='[[ $(find "$0" -maxdepth 2 |egrep "\.mp4|\.mp3|\.ogg|\.flac|\.wma|\.m4a"| wc -l) == '$2' ]] && echo "$0"'
find $1 -type d -exec sh -c "$COMMAND" {} \;

Es gab also ein paar Dinge, die mit Ihrem Skript nicht stimmten:

  1. Sie haben mindepthanstelle von verwendet maxdepth.
  2. Die Punkte (.) in Ihrem Egrep hätten mit jedem beliebigen Zeichen abgeglichen. Also .wmamit „Snowman.txt“.
  3. Den zweiten Test mussten Sie für den Typ „d“ nicht durchführen, da an den Shell-Befehl nur Verzeichnisse übergeben werden.

Anmerkungen zu meinem Skript:

  1. Die Verwendung ist:findodd.sh <top_folder> <no_of_files>
  2. Anführungszeichen sind wichtig. Die Definition von COMMANDlautet eigentlich 2 Zeichenfolgenliterale auf beiden Seiten des $2. Das ist wirklich wichtig.
  3. Es listet nur die Ordner auf, die die Dateien enthalten, nicht die Dateien selbst. Dazu müssten Sie das echo "$0"durch ein anderes ersetzen find.

Ich habe es jetzt auf einer Arch Linux-Maschine getestet und meine Shell ist „bash“, daher habe ich keine Ahnung, ob es unter OSX funktioniert, da nicht alle Shells gleich sind. :-)


FRÜHERER ERSTER VERSUCH:

Hmmmm. Ich weiß nicht, wie ähnlich OSX Unix/Linux ist, aber ich werde es mal versuchen.

Die Antwort auf beide Ihrer Fragen liegt meines Erachtens im ersten Test des Befehls „sh -c“. Dort steht:

$(find "$0" -mindepth 1 | wc -l) -eq 1

Um Ihrem Skript einen zweiten Parameter für die Anzahl der Dateien zu übergeben, sollten Sie einfach die „1“ in „$2“ ändern können. Der Test sähe dann folgendermaßen aus:

$(find "$0" -mindepth 1 | wc -l) -eq $2

Setzen Sie keine Anführungszeichen darum, $2da es sonst als zweiter Parameter interpretiert wird, der an den Befehl „sh -c“ und nicht an Ihr Skript übergeben wird.

Die Befehlszeile würde dann lauten:

./findodd.sh /Users/paul/Music 2

egrepUm Ihre zweite Anforderung zu erfüllen, müssen Sie, so wie ich es verstehe, den Befehl in den ersten Test einfügen , und zwar folgendermaßen:

$(find "$0" -mindepth 1 |egrep ".mp4|.mp3|.ogg|.flac|.wma|.m4a"| wc -l) -eq $2

Möglicherweise müssen Sie jedoch auf die Zitate achten.

Probieren Sie es auf jeden Fall aus und lassen Sie es uns wissen.

Antwort3

Sie können dies in Python implementieren, indem Sie etwa Folgendes tun:

#!/usr/bin/env python

import fnmatch
import os
import sys

if len(sys.argv) != 3 or \
        not sys.argv[1].isdigit() or \
        not os.path.exists(sys.argv[2]):
    print "Usage: %s [number of files] [search root]" % sys.argv[0]
    sys.exit(1)

num_files = int(sys.argv[1])
search_root = sys.argv[2]

# this must be a tuple to work with endswith()
audio_extensions = (
    'mp4',
    'mp3',
    'ogg',
    'flac',
    'wma',
    'm4a',
)

for dirpath, dirnames, filenames in os.walk(search_root):
    audio_files = [f for f in filenames if f.endswith(audio_extensions)]
    if len(audio_files) == num_files:
        print "\n".join([os.path.join(dirpath, f) for f in audio_files])

Wenn Sie chmod +x findodd.pyes können, können Sie es auf die gleiche Weise ausführen, wie Sie Ihr aktuelles Skript ausführen, zB:

./findodd.py 1 /Users/paul/Music

verwandte Informationen