3

У меня есть папка Music, и содержимое выглядит так:

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

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

4 ответа4

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"})

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

Group-Object -Property Directory 

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

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

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

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

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

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

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

./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 ] && echo "$1"' {} \;

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

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, повторяя его для каждого совпадения в одном и том же файле. Например, он проверяет музыку /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

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .