Bash:只有循环索引的最后一次迭代没有被存储为数组的新元素

时间:2011-05-27 18:36:14

标签: arrays bash sorting string for-loop

我有一个脚本在目录树中搜索音乐文件,根据特定属性(流派,艺术家,曲目,专辑)从音乐文件的完整路径名中提取相关信息,将格式化的子串存储为元素一个数组,然后根据在命令行上传递的选项对数组的元素进行排序(例如,按照专辑排序)。一切似乎都运行得很好,除了for循环的最后一次迭代的结果输出似乎没有被存储为数组中的新元素。在查找有关Bash数组的信息后,我发现数组大小没有限制。因此,我一直在摸不着为什么每个其他迭代的输出直到最后都存储在数组中。如果您查看下面的输出,您可以看到最后一个元素应该是“Tundra”轨道。

(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 个答案:

答案 0 :(得分:3)

迪伦,

你的作业给了我一个关于我的音乐库做点什么的想法,并且 我看到的不仅仅是你单独做作业,所以这里有一些评论 - 如何制作一个musicsort命令:)

shell编程的主要功能在于能够将作业流水线化并分解成小部件,而这些小部件可以轻松地一起播放。

因此,
更改列顺序不是一个好主意。请参阅示例ls -l。您想要对输出进行排序并不重要(例如,ls -lt =按时间,或ls -ltr =按时间但反转)列顺序保持不变。这样,您可以轻松地将输出从ls输出到另一个命令,而无需担心列顺序。如果你真的需要改变它,这里已经有了可以有效做到的工具。

我的第一个建议 - 不要更改输出列顺序,只按它们排序。

第二 - 标题行。只在真正需要的时候打印。 (例如,对于正常输出),当您以后想要从全新的musicsort命令进行流水线输出时,标题会导致很多问题。所以,

我的第二个建议 - 仅根据命令参数打印标题。

当我们分解你的问题时,我们得到:

  1. 需要一些命令行参数处理
  2. 需要设置一些默认值,如果这里没有参数
  3. 需要在音乐目录中找到音乐文件
  4. 需要按标准对其进行排序
  5. 需要按排序顺序打印它们
  6. 现在跳过1,2。

    在musicdir中查找文件很简单。

    find "$musicdir" -type f -print
    

    将打印出所有文件。 ofc,这里可以是一些封面图片和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可以按任何字段排序,并且可以告诉他什么是字段分隔符 E.g。

    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行是参数处理和其他事情。主要部分完成了几行。

    如果您确实需要更改列顺序,可以通过添加少量格式化行来轻松完成...