Jak znaleźć foldery zawierające pliki x określonego typu i wyprowadzić te ścieżki w OSX

2

Mam ten skrypt dla OSX do wyszukiwania folderów, które zawierają tylko jeden plik, i jeśli ten plik jest plikiem audio, który wysyła ścieżkę do pliku audio

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"

czyli używaj jak

./findodd.sh /Users/paul/Music

ale potrzebuję dwóch ulepszeń:

  1. Co mogę zmienić, aby wyświetlić pliki w folderach zawierających 2 pliki, 3 pliki ectera, byłoby nawet lepiej, gdyby można było przekazać je jako parametr

  2. Obecnie znajduje foldery zawierające tylko jeden plik, a ten plik musi być plikiem audio, ale naprawdę chcę, aby znalazł folder zawierający tylko jeden plik audio, tj. Jeśli folder zawiera trzy pliki, ale tylko jeden jest plikiem audio I chcesz, aby ten plik audio był na liście.

dzięki Paul

Paul Taylor
źródło
Sprawdzam datę
Ram
Aby uzyskać drugą część, o którą prosiłeś - Musisz tylko zmienić polecenia 1. Wykonaj filtrowanie plików audio, a następnie wykonaj obliczenia. Jeśli najpierw odfiltrujesz zawartość folderu, przejrzysz i zobaczysz 4 pliki, ale tylko ten, który jest rzeczywiście plikiem audio, zostanie przekazany do następnego polecenia, a pozostałe zostaną zignorowane -> Następnie się liczy i PING to jeden -> wydruk.
konqui

Odpowiedzi:

2
$ 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

Przykładowe przebiegi:

$ ./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

A tutaj kod:

#!/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

Iteruje po wszystkich podkatalogach danego katalogu i używa globbingu do odczytu wszystkich nazw plików audio bieżącego podkatalogu w tablicy files. Jeśli rozmiar tablicy jest zgodny z żądaną wartością, wypisuje nazwy plików oddzielone znakiem nowej linii.

EDYCJA: To jest moje wcześniejsze podejście oparte na założeniu, że chcesz wydrukować foldery, a nie nazwy plików. Zostawię to tutaj na przyszłość.

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

To, co robi, to znalezienie wszystkich plików z wymienionymi rozszerzeniami audio i wydrukowanie ich komponentów katalogowych zamiast pełnej ścieżki. Daje to listę folderów nadrzędnych dla wszystkich plików audio. W uniqpomija nieunikalne linii, które powinny dać wynik jesteś po to jest tylko drukowanie folderów, które zawierają dokładnie jeden plik audio.

Teoretycznie powinno to być trochę szybsze niż wcześniejsza próba.

Możesz to poprawić, aby zaspokoić swój pierwszy punkt, licząc zduplikowane linie i drukując tylko te foldery, które pasują do żądanej liczby. Naiwnym rozwiązaniem byłoby:

$ 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

chociaż może być lepiej uniqpołączyć jedną część i prawą stronę rury w jednej awklinii.

Adrian Frühwirth
źródło
Dzięki, ale twój podstawowy skrypt jest gorszy od oryginalnego, ponieważ wyświetla folder (którego nie chcę), wyprowadza ścieżki względne (których nie chcę) i nie działa na OSX (ponieważ OSX nie obsługuje -printf) opcji. Nie mam problemu z prędkością Po prostu chcę drobnego dostosowania do oryginalnego skryptu, aby naprawić punkt 1. i najlepiej punkt 2
Paul Taylor
@ PaulTaylor Masz rację, źle odczytałem twoje początkowe pytanie i pomyślałem, że chcesz wydrukować foldery, a nie pliki. Powodem, dla którego mój urywek zawiera ścieżki względne, jest to, że findwypisze ścieżki względne, jeśli poda ścieżki względne jako wejściowe i bezwzględne, jeśli poda ścieżki bezwzględne (a więc zależy od wywołania). Zaktualizowałem swój post i dodałem nowe rozwiązanie oparte na twoim wyjaśnieniu, o którym myślę, że masz na myśli. Kod jest tak krótki, jak twój początkowy fragment, ale spełnia oba wymagania, jeśli teraz je poprawnie rozumiem i powinien być bardziej czytelny :-)
Adrian Frühwirth
1

DRUGIE PODEJŚCIE

OK, po samodzielnym wypróbowaniu tego w moim własnym folderze Muzyka jest to rozwiązanie dla obu twoich żądań:

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" {} \;

W twoim skrypcie było kilka błędów:

  1. Używałeś mindepthzamiast maxdepth.
  2. Okresy (.) W twoim egrep pasowałyby do dowolnego znaku. Tak .wmaby pasowało do „Snowman.txt”.
  3. Nie trzeba było wykonywać drugiego testu dla typu „d”, ponieważ do polecenia powłoki przekazywane są tylko katalogi.

Uwagi na temat mojego skryptu:

  1. Wykorzystanie: findodd.sh <top_folder> <no_of_files>
  2. Cytaty są krytyczne. Definicja COMMANDjest w rzeczywistości 2 literałami ciągów po obu stronach $2. To naprawdę ważne.
  3. Wyświetla tylko foldery zawierające pliki, a nie same pliki. Aby to zrobić, musisz zastąpić echo "$0"drugi find.

Teraz testowałem na maszynie Arch Linux, a moja powłoka jest „bash”, więc nie mam pojęcia, czy to działa na OSX, ponieważ wszystkie powłoki NIE są sobie równe. :-)


WCZEŚNIEJSZA PIERWSZA PRÓBA:

Hmmmm. Nie wiem, jak podobny jest OSX do Uniksa / Linuksa, ale dam to w parze.

Odpowiedź na oba pytania, jak sądzę, leży w pierwszym teście polecenia „sh -c”. Oto fragment, który brzmi:

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

Aby przekazać drugi parametr do skryptu dla liczby plików, powinieneś być w stanie po prostu zmienić „1” na 2 $, więc test będzie:

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

Nie umieszczaj cudzysłowów wokół, $2ponieważ w przeciwnym razie zostanie zinterpretowany jako drugi parametr przekazany do polecenia „sh -c”, a nie do skryptu.

Linia poleceń byłaby wtedy:

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

Aby osiągnąć swoje drugie wymaganie, jak rozumiem, musisz umieścić egreppolecenie w pierwszym teście, a więc:

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

Być może będziesz musiał oglądać cytaty.

W każdym razie daj sobie spokój i daj nam znać.

DuncanKinnear
źródło
Dzięki niestety to nie działa, może coś OSX.
Paul Taylor
0

Możesz zaimplementować to w Pythonie, wykonując coś takiego:

#!/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])

Jeśli możesz chmod +x findodd.py, uruchom go w ten sam sposób, w jaki uruchamiasz swój bieżący skrypt, np .:

./findodd.py 1 /Users/paul/Music
pxul
źródło
Ciekawe, ale nie chcę wprowadzać Pythona do miksu
Paul Taylor