Bash: только последняя итерация индекса цикла не сохраняется как новый элемент массива - PullRequest
1 голос
/ 27 мая 2011

У меня есть скрипт, который ищет в дереве каталогов музыкальные файлы, извлекает соответствующую информацию из полного пути к музыкальным файлам в соответствии с определенными атрибутами (Genre, Artist, Track, Album), хранит отформатированные подстроки как элементы в массив, затем сортировка элементов массива на основе параметра, который был передан в командной строке (например, сортировка по альбому). Кажется, все работает хорошо, за исключением того, что результат последней итерации цикла for, похоже, не сохраняется как новый элемент в массиве. Посмотрев информацию о массивах Bash, я обнаружил, что нет никаких ограничений на размер массива. Таким образом, я остаюсь почесывая голову, почему каждый вывод каждой итерации, вплоть до последней, сохраняется в массиве. Если вы посмотрите на вывод ниже, вы увидите, что последним элементом должна быть дорожка «Тундра».

(more output above ...)

-->./Hip-Hop/OutKast/Stankonia/Toilet Tisha.aif<-- 
GENRE:
Hip-Hop
ARTIST:
OutKast
ALBUM:
Stankonia
TITLE:
Toilet Tisha

-->./Electronic/Squarepusher/Hard Normal Daddy/Cooper's World.aif<--
GENRE:
Electronic
ARTIST:
Squarepusher
ALBUM:
Hard Normal Daddy
TITLE:
Cooper's World

-->./Electronic/Squarepusher/Hard Normal Daddy/Papalon.aif<--
GENRE:
Electronic
ARTIST:
Squarepusher
ALBUM:
Hard Normal Daddy
TITLE:
Papalon

-->./Electronic/Squarepusher/Hard Normal Daddy/Vic Acid.aif<--
GENRE:
Electronic
ARTIST:
Squarepusher
ALBUM:
Hard Normal Daddy
TITLE:
Vic Acid

-->./Electronic/Squarepusher/Go Plastic/Go! Spastic.aif<--
GENRE:
Electronic
ARTIST:
Squarepusher
ALBUM:
Go Plastic
TITLE:
Go! Spastic

-->./Electronic/Squarepusher/Go Plastic/Greenways Trajectory.aif<--
GENRE:
Electronic
ARTIST:
Squarepusher
ALBUM:
Go Plastic
TITLE:
Greenways Trajectory

-->./Electronic/Squarepusher/Go Plastic/My Red Hot Car.aif<--
GENRE:
Electronic
ARTIST:
Squarepusher
ALBUM:
Go Plastic
TITLE:
My Red Hot Car

-->./Electronic/Squarepusher/Feed Me Weird Things/Kodack.aif<--
GENRE:
Electronic
ARTIST:
Squarepusher
ALBUM:
Feed Me Weird Things
TITLE:
Kodack

-->./Electronic/Squarepusher/Feed Me Weird Things/North Circular.aif<--
GENRE:
Electronic
ARTIST:
Squarepusher
ALBUM:
Feed Me Weird Things
TITLE:
North Circular

-->./Electronic/Squarepusher/Feed Me Weird Things/Tundra.aif<--
GENRE:
Electronic
ARTIST:
Squarepusher
ALBUM:
Feed Me Weird Things
TITLE:
Tundra

Как видите, последняя итерация в разделе DEBUG должна называться "Tundra". Однако, когда я отображаю содержимое массива «track_list», каждая дорожка печатается в нужном формате, кроме «Tundra» (см. Вложенный файл .png). Кто-нибудь знает, почему это может быть? Вот часть моего сценария: enter image description here

#more code above ...

#create arrays
declare -a track_list
declare -a directory_contents


#populate directory with files
cd $directory
directory_contents=$(find .  -mindepth 1 -type f)
cd ..


IFS=$'\n'


#store each file of directory in track_list
for music_file in ${directory_contents[*]}; do
    if [ -n "$DEBUG" ] ; then echo "-->$music_file<--"; fi

    this_genre=$(echo $music_file | awk -F"/" '{print $2}')
    this_artist=$(echo $music_file | awk -F"/" '{print $3}')
    this_album=$(echo $music_file | awk -F"/" '{print $4}')
    this_title=$(echo $music_file | awk -F"/" '{print $5}' |\
        awk -F".aif" '{print $1}' || awk -F".mp3" '{print $1}' ||\
        awk -F".wav" '{print $1}' || awk -F".flac" '{print $1}' \
        || awk -F".m4a" '{print $1}')

    function artist_list
    {
    track=$(printf "%-20s\t\t%-30s\t\t%-30s\t\t%-10s\n"\
            "$this_artist" "$this_title" "$this_album" "$this_genre")
    track_list=("${track_list[*]}" $track)
    }

    if [[ $genre = "true" ]] ; then
    track=$(printf "%-10s\t\t%-20s\t\t%-30s\t\t%-30s\n"\
            "$this_genre" "$this_artist" "$this_title" "$this_album")
    track_list=("${track_list[*]}" $track)
    elif [[ $artist = "true" ]] ; then
    artist_list
    elif [[ $album = "true" ]] ; then
    track=$(printf "%-30s\t\t%-20s\t\t%-30s\t\t%-10s\n"\
            "$this_album" "$this_artist" "$this_title" "$this_genre")
    track_list=("${track_list[*]}" $track)
    elif [[ $title = "true" ]] ; then
    track=$(printf "%-30s\t\t%-20s\t\t%-30s\t\t%-10s\n"\
            "$this_title" "$this_artist" "$this_album" "$this_genre")
    track_list=("${track_list[*]}" $track)
    else
    artist_list
    fi

    if [ -n "$DEBUG" ]; then
    echo "GENRE:"
    echo $this_genre
    echo "ARTIST:"
    echo $this_artist
    echo "ALBUM:"
        echo $this_album
    echo "TITLE:"
    echo $this_title
    echo ""
    fi
done


unset IFS


if [[ $genre = "true" ]] ; then
    ./mulib g
elif [[ $artist = "true" ]] ; then
    ./mulib a
elif [[ $album = "true" ]] ; then
    ./mulib m
elif [[ $title = "true" ]] ; then
    ./mulib t
else
    ./mulib
fi

echo "$track_list" | sort
echo ""

1 Ответ

3 голосов
/ 28 мая 2011

Дилан,

Ваша домашняя работа дает мне идею сделать что-то и с моей музыкальной библиотекой, и я вижу, чем вы пытаетесь сделать свою домашнюю работу в одиночку, поэтому вот некоторые комментарии - как сделать musicsortкоманда:)

Основная сила программирования оболочки заключается в его способности конвейеризовать и разбивать работу на маленькие части, в то время как эти маленькие части могут легко играть вместе.

, следовательно,
Этоне очень хорошая идея менять порядок столбцов.См. Например ls -l.Неважно, по каким параметрам вы хотите отсортировать вывод (например, ls -lt = по времени или ls -ltr = по времени, но в обратном порядке), порядок столбцов остается неизменным.Таким образом, вы можете легко передать вывод команды ls другой команде, не беспокоясь о порядке столбцов.И если вам действительно нужно изменить это, вот уже инструменты, которые могут сделать это эффективно.

Мое первое предложение - не меняйте порядок выходных столбцов, сортируйте только по ним.

Второй - строка заголовка.Печать ставить только тогда, когда действительно этого захочешь.(например, для нормального вывода), потому что, когда вам позже понадобится конвейеризовать вывод вашей новой команды musicsort, заголовки вызовут много проблем.Итак,

мое второе предложение - печатать заголовок только на основе аргумента команды.

Когда мы разбираем вашу проблему, мы получаем:

  1. нужен некоторый аргумент командной строкиобработка
  2. необходимо установить некоторые значения по умолчанию, если здесь нет аргументов
  3. нужно найти музыкальные файлы в вашей музыкальной директории
  4. нужно отсортировать их по критериям
  5. нужно распечататьони - в отсортированном порядке

Пропуск 1,2 на данный момент.

Поиск файлов в вашем музыкальном каталоге очень прост.

find "$musicdir" -type f -print

распечатает все файлы рекурсивно,Конечно, здесь могут быть некоторые изображения обложек и текст txt, поэтому нужно их отфильтровать, например, с помощью

find "$musicdir" -type f -print | egrep -i '\.(mp3|aif*|m4p|wav|flac)$'

у нас есть все ваши музыкальные файлы.Хорошо разделенный символом / в следующем порядке:

/path/to/musicdir/genre/artist/album/track.suffix

. Для вывода нам нужно удалить / path / to / musicdir /.Это легко.Вот еще один способ, например sed.

sed "s:^$musicdir/::;s:\.[^/][^/]*$::"

Вышеуказанная команда выполняет две вещи: 1.) удаляет путь $ musicdir из вашего списка файлов и тоже удаляет любой .suffix.(например .mp3 .flac и т. д.).Результат:

genre/artist/album/track

Хорошая строка, четко разделенная - такая сортируемая.Для сортировки у нас есть команда sort.sort может сортировать по любому полю и, возможно, сказать ему, что такое разделитель полей.
Например,

sort -df -t/ -k2,2

отсортирует ввод, разделенный знаком '/', вторым полем (исполнитель).Для -df смотрите man sort.

и, наконец, нам нужно прочитать уже отсортированный список файлов в переменные и распечатать.(здесь, конечно, есть и другой способ).Для этого в bash есть команда read.И мы должны сказать bash, каков его временный разделитель полей (IFS), так:

IFS=/; read genre artist album track

и поскольку у нас больше строк на входе, мы должны делать это в цикле, тогда как у нас есть строки на входе.

Окончательный сценарий находится здесь:

musicdir="."
FORMAT="%-20s%-35s%-35s%-35s\n"
sortby=2 #for this example - artist

find "$musicdir" -type f -print |\
egrep -i '\.(aif*|mp3|flac|m4a|wav)$' |\
sed "s:^$musicdir/::;s:\.[^/][^/]*$::" |\
sort -t/ -k$sortby,$sortby | (
IFS=/; while read genre artist album track
do
        printf "$FORMAT" $genre $artist $album $track
done)

Как видите, весь поиск, сортировка, печать выполняется в несколько строк.(части 3, 4, 5).

Для финала нужно сделать некоторую обработку аргументов.Я написал один, это не на 100% нормально, но работает.

Последний скрипт, который может обрабатывать некоторые аргументы, устанавливать значения по умолчанию и выполнять основные функции, может выглядеть следующим образом: (ofc, здесь возможновыполнить оптимизацию zilion, например, объединить egrep и sed только в один sed и т. д.)

#!/bin/bash
#argument handling - not 100% correct, but working...
while getopts "hHgaltd:" arg
do
case "$arg" in
    g) sortby=1;;
    a) sortby=2;;
    l) sortby=3;;
    t) sortby=4;;
    d) musicdir=$OPTARG;;
    H) header=y;;
    h|?)    echo "Usage: $0 [-d music_dir] [-g|-a|-l|-t] [-H]";
        echo ' -d music_dir = path to your music directory (default ".")'
        echo ' -g|-a|-l|-t = for sorting by Genre/Artist/aLbum/Track (default "-a")'
        echo ' -H print header (default no)'
        exit 1;;
esac
done

#defaults
sortby=${sortby:=2};
musicdir=${musicdir:=.}
FORMAT="%-20s%-35s%-35s%-35s\n"

#header only if want one
if [[ $header == "y" ]]
then
    printf "$FORMAT" genre artist album track
    printf -v line '%*s' 125; echo ${line// /-}
fi

#the main part - search, sort, read into variables, print
find "$musicdir" -type f -print |\
egrep -i '\.(aif*|mp3|flac|m4a|wav)$' |\
sed "s:^$musicdir/::;s:\.[^/][^/]*$::" |\
sort -t/ -k$sortby,$sortby | (
IFS=/; while read genre artist album track
do
    printf "$FORMAT" $genre $artist $album $track
done)

, например,

$ musicsort -t -H -d .

будет производить вывод, отсортированный подорожки, заголовок печати и музыка находятся в текущем каталоге

genre               artist                             album                              track                              
-----------------------------------------------------------------------------------------------------------------------------
Electronic          Squarepusher                       Hard Normal Daddy                  Cooper's World                     
Electronic          Squarepusher                       Go Plastic                         Go! Spastic                        
Electronic          Squarepusher                       Go Plastic                         Greenways Trajectory               
Electronic          Squarepusher                       Feed Me Weird Things               Kodack                             
Electronic          Squarepusher                       Go Plastic                         My Red Hot Car                     
Electronic          Squarepusher                       Feed Me Weird Things               North Circular                     
Electronic          Squarepusher                       Hard Normal Daddy                  Papalon                            
Hip-Hop             OutKast                            Stankonia                          Toilet Tisha                       
Electronic          Squarepusher                       Feed Me Weird Things               Tundra                             
Electronic          Squarepusher                       Hard Normal Daddy                  Vic Acid        

Как видите, 3/4 строки - это обработка аргументов и другие вещи.Основная часть выполняется в несколько строк.

Если вам действительно нужно изменить порядок столбцов, это легко сделать, добавив несколько строк форматирования ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...