
У меня есть папка «Музыка», содержимое которой выглядит так:
Музыка -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