Сортируем MP3
При скачивании MP3 из сети я обычно складываю их в одну папку, поскольку качает их в фоновом режиме менеджер закачек. Так скачивать удобно тем, что определив в его настройках скачивать не больше одного-двух файлов одновременно, можно при этом параллельно работать в сети.
Поскольку при этом MP3-файлов накапливается в одной папке довольно много, то я решил, что стоит написать shell-скрипт, который бы их рассортировывал по папкам согласно классической схеме: верхний каталог - по тегу Artist, а вложенные - по тегу Album. И так как у большинства артистов набирается всего по несколько файлов от каждого, то раскладывать файлы по альбомам я решил сделать опцией.
Вот, что получилось в результате.
Сперва я написал функцию has_option(), которая проверяет, есть ли в параметрах вызова скрипта та или иная опция:
function has_option() # locate script argument
{
result=no
for op in $1 ; do
if [ "x$op" = "x$2" ] ; then # short option
result=yes
return
fi
if [ "x$op" = "x$3" ] ; then # long option
result=yes
return
fi
done
}
Функция принимает три аргумента - строку с ключами ($1), короткий ключ ($2) и длинный ключ ($3). Например, стандартный ключ запроса подсказки может быть представлен как коротким ключом -h, так и длинным --help. Вот функция has_option() может проверять как наличие короткого ключа, так и длинного. При этом она присваивает переменной $result значение либо yes, либо no.
Следующая функция, opts_albums(), проверяет, задан ли ключ -a (сортировать по альбомам), и если да, то присваивает переменной $albums значение либо yes, либо no:
function opts_albums()
{
albums=no
has_option "$*" -a
if [ $result = "yes" ] ; then
albums=yes
fi
}
Далее я просто вызвал эту функцию:
opts_albums "$*"
Чтобы особо не утруждать себя, какой программой читать теги в MP3-файлах, я решил брать эту информацию из вывода mpg123, и обрабатывать с помощью awk. Чтобы mpg123 не проигрывал файл, я запускаю его с ключом -t (тестирование), а вывод с stderr перенаправляю в файл, чтобы не декодировать MP3-шник два раза:
mpg123 -t $fn 2>${fn}'.txt'
Затем я составил два почти идентичных скрипта для awk с той лишь разницей, что один выполняется для тега Artist, а другой - для тега Album:
artist_script='/Artist:/ {
str = $0;
n = index(str, "Artist:");
an = substr(str, n+7);
sub(/^[ \t]+/, "", an);
sub(/[ \t]+$/, "", an);
gsub(/[ \t\n\+]/, "_", an);
gsub(/[^0-9A-Za-z_\-]/, "q", an);
print an
}'
album_script='/Album:/ {
str = $0;
n = index(str, "Album:");
an = substr(str, n+6);
sub(/^[ \t]+/, "", an);
sub(/[ \t]+$/, "", an);
gsub(/[ \t\n\+]/, "_", an);
gsub(/[^0-9A-Za-z_\-]/, "q", an);
print an
}'
Делают они следующее. Находят в строке текст, вырезают текст за ним, удаляют пробелы и табуляции в начале и конце этих текстов, заменяют некоторые символы символом подчёркивания, а затем все прочие символы, которые могут вызвать вопросы при использовании этих литералов в качестве имён файлов, заменяются буквой q.
После этого я написал простой цикл, в котором обрабатывается вывод команды ls *.mp3.
Для использования скрипта я поместил его в каталог $HOME/bin. Чтобы рассортировать MP3-шки с его помощью, я перехожу в папку с MP3-шками, открываю эмулятор терминала и вызываю в ней свой скрипт. Скрипт создаёт подкаталоги по именам артистов, а если требуется, то и по названиям альбомов, и копирует в эти каталоги соответствующие файлы.
Таким образом, на каждой итерации цикла получается имя каталога для артиста, проверяется, есть ли такой файл, и если есть, то каталог ли это, и если да, то задан ли ключ -a, и если да, то тоже самое - для названия альбома:
for fn in `ls *.mp3` ; do
echo "File: $fn"
dfn=$fn'.txt'
mpg123 -t $fn 2>$dfn
dn=`cat $dfn | awk "$artist_script"`
if [ "x$dn" != "x" ] ; then
echo " Artist directory: $dn"
if [ ! -e $dn ] ; then
mkdir $dn ;
fi
if [ -d $dn ] ; then
nfn=${dn}/$fn
if [ $albums = "yes" ] ; then
adn=`cat $dfn | awk "$album_script"`
if [ "x$adn" != "x" ] ; then
echo " Album directory: $adn"
if [ ! -e ${dn}/$adn ] ; then
mkdir ${dn}/$adn ;
fi
if [ -d ${dn}/$adn ] ; then
nfn=${dn}/${adn}/$fn ;
fi
fi
fi
mv $fn $nfn ;
fi
fi
rm $dfn
done
Выглядит жутковато, но это потому, что лень было дополнительные переменные объявлять в целях повышения читаемости кода. Скачать скрипт.
Автор: Андрей Шаройко <vanyamboe@gmail.com>