У Linux або Windows, як знайти шлях для всіх mp3-файлів, які є єдиними mp3-файлами в цьому каталозі?


3

У мене є папка "Музика", а вміст виглядає так:

Music
    -ArtistA
        -Song1.mp3
        -AlbumA
            -Song2.mp3
            -Song2.m4a
        -AlbumB
            -Song1.mp3
            -Song2.mp3
            -Song3.mp3
        -AlbumC
            -Song1.mp3
            -Song1.jpg
            -SomeFolder1
                -Song1.mp3
    -ArtistB
        -Song1.mp3
    -ArtistC
        -Song1.mp3
        -Song2.mp3
        -Song3.mp3
        -SomeFolder2
            -Song1.jpg
            -Song2.jpg
        -SomeFolder3


Я шукаю такий вивід:

ArtistA/Song1.mp3
ArtistA/AlbumA/Song2.mp3
ArtistA/AlbumC/Song1.mp3
ArtistA/AlbumC/SomeFolder1/Song1.mp3
ArtistB/Song1.mp3



Таким чином, я не хочу каталогів з одним файлом, я хочу каталоги з одним mp3 - незалежно від підкаталогів або інших файлів. Я також не хочу порожніх каталогів або каталогів, які містять більше одного mp3.

Я не проти скрипту оболонки для Linux. Однак я не хочу компілювати програму ні на одній з ОС.

Я намагався: find . -type d -exec sh -c 'set -- "$0"/*.mp3; [ $# -le 1 ]' {} \; -print

але це дає мені такий висновок:

.
./ArtistB
./ArtistA
./ArtistA/AlbumA
./ArtistA/AlbumC
./ArtistA/AlbumC/SomeFolder1
./ArtistC/SomeFolder2
./ArtistC/SomeFolder3

що близьке, але не зовсім те, що я хочу. Є дві порожні папки і немає назв файлів.

Відповіді:


3

На перший погляд, причина, чому ваша перша команда повідомляє про порожні папки (ті, у кого немає *.mp3 що ви говорите -le 1 а не -eq 1. Це виглядає так, що він повідомлятиме каталоги з одним файлом MP3 або менше. Але змінюється -le 1 до -eq 1 не працює. Виявляється, що проблема в каталозі з ≥ одним файлом MP3, ім'я_каталогу /*.mp3 отримує список імен файлів MP3, але, у каталозі, у якому немає жодного, він залишається, буквально, як ім'я_каталогу /*.mp3 . Це можна виправити, вказавши оболонку що шаблони, які не відповідають жодним файлам, повинні просто зникнути з командного рядка:

find . -type d -exec
        sh -c ' shopt-s nullglob;  set - "$ 0" / *. mp3; [$ # -eq 1] '{} \ t -принт 

(набрано все як один рядок)

./ArtistA
./ArtistA/AlbumA
./ArtistA/AlbumC
./ArtistA/AlbumC/SomeFolder1
./ArtistB

Але у вас є оболонка, яка перераховує файли, і ви запитуєте find для друку - і find шукає лише в каталогах. Чому б не дозволити оболонці роздрукувати ім'я файлу, який він бачить?

find . -type d -exec
        sh -c 'shopt -s nullglob; set -- "$0"/*.mp3; [ $# -eq 1 ]  & amp; & amp; луна "$ 1"  '{} \ T 

(тобто, якщо є один файл, echo ім'я першого ( $1 )) дасть вам те, що ви хочете.


4

Для Windows за допомогою PowerShell, якщо ви вже перебуваєте в папці C: YOUR_PATH:

PS C:\YOUR_PATH> (Get-ChildItem -recurse | Where-Object {$_.name -match ".mp3"}) | Group-Object -Property Directory | Where-Object {$_.Count -eq 1} | ForEach-Object { (Get-ChildItem $_.Name -Filter "*.mp3").FullName}

Виходи:

C:\YOUR_PATH\ArtistA\Song1.mp3
C:\YOUR_PATH\ArtistA\AlbumA\Song2.mp3
C:\YOUR_PATH\ArtistA\AlbumC\Song1.mp3
C:\YOUR_PATH\ArtistA\AlbumC\SomeFolder1/Song1.mp3
C:\YOUR_PATH\ArtistB\Song1.mp3

Розбиття команди:

Отримати всі файли та папки під поточним каталогом

Get-ChildItem -recurse 

Зіставляйте тільки файли на основі вашої маски

Where-Object {$_.name -match ".mp3"})

Виконайте групування на основі властивості Directory

Group-Object -Property Directory 

Виберіть лише каталоги з 1 елементом

Where-Object {$_.Count -eq 1} 

Витягніть повне ім'я одного файлу, що відповідає вашому файлу у кожному каталозі

ForEach-Object { (Get-ChildItem $_.Name -Filter "*.mp3").FullName} 

1

Я впевнений, що хтось має кращий спосіб зробити це, але ця команда bash буде працювати на Linux.

for Folder in $(find . -name "*.mp3" -printf "%h\n")
do
    [[ $(ls -l $Folder/*.mp3 | wc -l) -eq "1" ]] && ls $Folder/*.mp3
done

Цей скрипт виконує тест на папку кожного файлу .mp3, повторюючись для кожного збігу в одному файлі. Наприклад, 3 рази перевіряє Music / ArtistA / AlbumB. Залежно від кількості mp3-файлів, це може тривати довго (вам знадобиться досить велика бібліотека).

Якщо так, ви можете зробити це трохи складніше:

for Folder in $(find . -name "*.mp3" -printf "%h\n" | awk '!x[$0]++')
    do
        [[ $(ls -l $Folder/*.mp3 | wc -l) -eq "1" ]] && ls $Folder/*.mp3
    done

Результат такий самий, але він перевірятиме кожну папку один раз, що може заощадити багато часу. Команда awk видаляє будь-які повторювані імена папок.

Інша примітка в тому, що файли чутливі до регістру в Linux, так що це буде повністю ігнорувати будь-які .MP3 або .Mp3 файлів. Якщо ви хочете, щоб вони відповідали цим, змініть всі 3 примірники mp3 до [mM][pP]3


protip: для того, щоб зробити пошук нечутливим відповідності для імен файлів, можна використовувати -iname & lt; pattern & gt; замість -name & lt; pattern & gt ;.
webmarc

Правда, але вона потребує регулярних виразів у командах ls так чи інакше, тому я подумав, що це буде послідовно.
Omnipresence

0

Ось відповідь:

find Music -name "*.mp3" -exec dirname {} \; |
    sort |
    uniq -c |
        while read count parent
        do
            if [[ $count -eq 1 ]]
            then
                echo "$parent"/*.mp3
            fi
        done

Спробуйте замінити внутрішню find с echo "$parent"/*.mp3.
G-Man
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.