我有一个脚本在目录树中搜索音乐文件,根据特定属性(流派,艺术家,曲目,专辑)从音乐文件的完整路径名中提取相关信息,将格式化的子串存储为元素一个数组,然后根据在命令行上传递的选项对数组的元素进行排序(例如,按照专辑排序)。一切似乎都运行得很好,除了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文件)之外,每个轨道都以所需的格式打印。有没有人知道为什么会这样?以下是我脚本的一部分:
#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 ""
答案 0 :(得分:3)
迪伦,
你的作业给了我一个关于我的音乐库做点什么的想法,并且
我看到的不仅仅是你单独做作业,所以这里有一些评论 - 如何制作一个musicsort
命令:)
shell编程的主要功能在于能够将作业流水线化并分解成小部件,而这些小部件可以轻松地一起播放。
因此,
更改列顺序不是一个好主意。请参阅示例ls -l
。您想要对输出进行排序并不重要(例如,ls -lt =按时间,或ls -ltr =按时间但反转)列顺序保持不变。这样,您可以轻松地将输出从ls输出到另一个命令,而无需担心列顺序。如果你真的需要改变它,这里已经有了可以有效做到的工具。
我的第一个建议 - 不要更改输出列顺序,只按它们排序。
第二 - 标题行。只在真正需要的时候打印。 (例如,对于正常输出),当您以后想要从全新的musicsort
命令进行流水线输出时,标题会导致很多问题。所以,
我的第二个建议 - 仅根据命令参数打印标题。
当我们分解你的问题时,我们得到:
现在跳过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行是参数处理和其他事情。主要部分完成了几行。
如果您确实需要更改列顺序,可以通过添加少量格式化行来轻松完成...