[英]Move mp3 files from one directory to separate directories by artist, album, song name
I have a backup of all my mp3s files in one folder. 我在一个文件夹中备份了所有mp3文件。
Would there be a bash command to automatically move all files in their own band-album title directories that would be created on the fly? 是否会有bash命令自动将所有文件自动移动到自己创建的乐队专辑标题目录中?
As you can see in the image, first words before dash are artist names, then comes the album title, then comes the name of the song. 如您在图像中看到的,破折号前的第一个单词是歌手姓名,然后是专辑名称,然后是歌曲名称。
Example: Artist or band - album title - name of song.mp3. 示例:艺术家或乐队-专辑标题-song.mp3的名称。
So in the end, files would be in the following hierarchy. 因此,最后,文件将处于以下层次结构中。
Artist or band1/
album title1/
name of song.mp3
album title2/
name of song.mp3
Artist or band2/
album title1/
name of song.mp3
and so forth. 等等。
I don't know of a command to do this directly, but here's how I would solve this (using Perl): 我不知道直接执行此操作的命令,但是这是我(使用Perl)解决此问题的方法:
perl -MFile::Path -we 'for my $file (glob "*.mp3") { my ($artist, $album, $title) = split / - /, $file, 3; mkpath "$artist/$album"; my $new = "$artist/$album/$title"; rename $file, $new or warn "$file -> $new: $!\n"; }'
Or slightly more readable: 或者更具可读性:
perl -MFile::Path -we '
for my $file (glob "*.mp3") {
my ($artist, $album, $title) = split / - /, $file, 3;
mkpath "$artist/$album";
my $new = "$artist/$album/$title";
rename $file, $new or die "$file -> $new: $!\n";
}'
I would do it as follows in Bash: 我会在Bash中执行以下操作:
#!/bin/bash
# Set field separator to dash
IFS=-
# Loop over mp3 files
for song in *.mp3; do
# Read long name into dash separated array
read -a songinfo <<< "$song"
# Remove trailing space from band name
band=${songinfo[0]% }
# Remove trailing and leading space from album name
album=${songinfo[1]% }
album=${album# }
# Remove leading space from song title
title=${songinfo[2]# }
# Make band/album directory, don't complain if they exist already
mkdir --parents "$band/$album"
# Move and rename song
mv "$song" "$band/$album/$title"
done
This changes the IFS
variable, but since this will be running in a child process, I didn't bother resetting it to its original value. 这将更改
IFS
变量,但是由于它将在子进程中运行,因此我无需费心将其重置为其原始值。
It's a bit lengthy due to parameter expansions to remove spaces, and it of course breaks if there is a dash in places other than between band/album/song names. 由于用于扩展空格的参数扩展,它有点长,并且如果在乐队/专辑/歌曲名称之间的其他地方没有破折号,则它当然会中断。 For a Bash solution that also works with dashes in other places, see mklement0's answer .
有关在其他地方也可以使用破折号的Bash解决方案,请参见mklement0的答案 。
melpomene's robust and efficient Perl solution is the best solution. melpomene强大而有效的Perl解决方案是最好的解决方案。
Here's a pure Bash implementation (except for calls to external utilities mkdir
and mv
) that is also robust with respect to avoiding false -
positives: 这里是一个纯粹的Bash实现(除调用外部公用事业
mkdir
和mv
),这也是相对于避免假稳健-
阳性:
for fname in *.mp3; do
IFS=/ read -r artist album song <<<"${fname// - //}"
song="${song//// - }"
mkdir -p "$artist/$album"
mv "$fname" "$artist/$album/$song"
done
${fname// - //}
uses Bash parameter expansion to replace all ( //
) <space>-<space>
sequences with ( /
) a /
char. ${fname// - //}
使用Bash参数扩展将所有( //
) <space>-<space>
序列替换为( /
)a /
char。 each. 每。
/
was chosen because it is guaranteed not to be contained in the input filenames. /
是因为它保证不会包含在输入文件名中。 The result is fed to read
via a here-string ( <<<
); 结果通过此处字符串(
<<<
) read
。 $IFS
, the internal field separator, is set to the auxiliary separator /
in order to split the filename into its constituent tokens. $IFS
(内部字段分隔符)设置为辅助分隔符/
,以便将文件名拆分为其组成标记。
Note that by specifying 3 variable names, the last variable specified receives the remainder of the input, even if it contains further instances of the separator. 请注意,通过指定3个变量名称,指定的最后一个变量将接收输入的其余部分,即使它包含分隔符的其他实例。
To be safe, song="${song//// - }"
then converts instances of /
back to <space>-<space>
sequences, so as to ultimately preserve the song part unmodified, should it contain such sequences. 为了安全起见,
song="${song//// - }"
然后将/
实例转换回<space>-<space>
序列,以便最终保留未修改的歌曲部分(如果包含这样的序列)。
mkdir -p "$artist/$album"
then creates subfolders for the artist and album; mkdir -p "$artist/$album"
然后为艺术家和专辑创建子文件夹; note that mkdir -p
is a (successful) no-op if the target folder already exists. 请注意,如果目标文件夹已存在,则
mkdir -p
是(成功)无操作。
Finally, the mv
command moves the input file to the target folder under its song-title-only name. 最后,
mv
命令将输入文件以其仅具有歌曲标题的名称移动到目标文件夹。
bash solution , to also take point into consideration that dash(-)
is also present in the artist names( All-Time Quarterback ). bash解决方案,还要考虑艺术家名称( All-Time Quarterback )中也出现了
dash(-)
)。
#!/bin/bash
ls *.mp3 > /home/user/mp3songs.list
awk '{print "\""$0"\""}' /home/user/mp3songs.list
while read -r line; do
song="$line"
artist=`echo "$song"|awk -F" - " '{print$1}'`
title=`echo "$song"|awk -F" - " '{print$2}'`
name=`echo "$song"|awk -F" - " '{print$3}'`
mkdir -p "$artist"
mkdir -p "$artist"/"$title"
printf "Moving $song to $artist/$title directory"
mv "$song" "$artist"/"$title"/"$name"
done < /home/user/mp3songs.list
rm /home/user/mp3songs.list
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.