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:
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
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. uniq
Nicht 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
uniq
Allerdings 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:
- Sie haben
mindepth
anstelle von verwendetmaxdepth
. - Die Punkte (.) in Ihrem Egrep hätten mit jedem beliebigen Zeichen abgeglichen. Also
.wma
mit „Snowman.txt“. - 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:
- Die Verwendung ist:
findodd.sh <top_folder> <no_of_files>
- Anführungszeichen sind wichtig. Die Definition von
COMMAND
lautet eigentlich 2 Zeichenfolgenliterale auf beiden Seiten des$2
. Das ist wirklich wichtig. - Es listet nur die Ordner auf, die die Dateien enthalten, nicht die Dateien selbst. Dazu müssten Sie das
echo "$0"
durch ein anderes ersetzenfind
.
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, $2
da 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
egrep
Um 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.py
es 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