Как в Linux или Windows найти путь ко всем mp3-файлам, которые являются единственными mp3-файлами в этом каталоге?

Как в Linux или Windows найти путь ко всем mp3-файлам, которые являются единственными mp3-файлами в этом каталоге?

У меня есть папка «Музыка», содержимое которой выглядит так:

Музыка
    -ArtistA
        -Песня1.mp3
        -АльбомА
            -Песня2.mp3
            -Песня2.m4a
        -АльбомБ
            -Песня1.mp3
            -Песня2.mp3
            -Песня3.mp3
        -АльбомC
            -Песня1.mp3
            -Песня1.jpg
            -SomeFolder1
                -Песня1.mp3
    -ArtistB
        -Песня1.mp3
    -ArtistC
        -Песня1.mp3
        -Песня2.mp3
        -Песня3.mp3
        -SomeFolder2
            -Песня1.jpg
            -Песня2.jpg
        -SomeFolder3


Я ищу вывод вроде этого:

ИсполнительA/Песня1.mp3
ИсполнительA/АльбомA/Песня2.mp3
ИсполнительA/АльбомC/Песня1.mp3
ИсполнительA/АльбомC/Папка1/Песня1.mp3
ИсполнительB/Песня1.mp3



Итак, мне не нужны каталоги с одним файлом, мне нужны каталоги с одним mp3 - независимо от подкаталогов или других файлов. Мне также не нужны пустые каталоги или каталоги, содержащие более одного mp3.

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

Я пробовал, find . -type d -exec sh -c 'set -- "$0"/*.mp3; [ $# -le 1 ]' {} \; -print

но это дает мне такой результат:

.
./ArtistB
./ХудожникА
./ИсполнительA/АльбомA
./ИсполнительA/АльбомC
./ИсполнительA/АльбомC/Папка1
./ArtistC/SomeFolder2
./ArtistC/SomeFolder3

что близко, но не совсем то, что я хочу. Есть две пустые папки и нет имен файлов.

решение1

На первый взгляд, причина, по которой ваша первая команда сообщает о пустых папках (без *.mp3файлов), заключается в том, что вы говорите -le 1вместо -eq 1. Похоже, что это будет сообщать о каталогах с одним файлом MP3 или меньше. Но изменение -le 1на -eq 1не работает. Оказывается, проблема в том, что в каталоге с ≥ одним файлом MP3 directory_name/*.mp3выводится список имен файлов MP3, но в каталоге без файлов он остается буквально как directory_name/*.mp3. Это можно исправить, указав оболочке, что подстановочные знаки, которые не соответствуют ни одному файлу, должны просто исчезнуть из командной строки:

найти . -тип d -exec
        ш -с 'shopt -s nullglob;установить -- "$0"/*.mp3; [ $# -eq 1 ]' {} \; -print

(набрано все в одну строку) дает

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

Но у вас есть оболочка, подсчитывающая файлы, и вы просите findсделать печать – и findона смотрит только на каталоги. Почему бы просто не позволить оболочке напечатать имя файла, которое она видит?

найти . -тип d -exec
        sh -c 'shopt -s nullglob; установить -- "$0"/*.mp3; [ $# -eq 1 ]&& эхо "$1"' {} \;

(т.е. если есть один файл, то echoимя первого из них ( $1)) даст вам то, что вы хотите.

решение2

Для 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"})

Выполнить группировку на основе свойства каталога

Group-Object -Property Directory 

Выбрать только каталоги с 1 элементом

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

Извлеките полное имя одного файла, соответствующего вашей маске файла в каждом каталоге.

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

решение3

Я уверен, что кто-то знает лучший способ сделать это, но эта команда 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, повторяя себя для каждого совпадения в одном и том же файле. Например, он проверяет Music/ArtistA/AlbumB 3 раза. В зависимости от того, сколько у вас файлов 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

решение4

Вот ответ:

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

Связанный контент