我想将 mp3 文件从目录复制到闪存驱动器。它们存储在如下目录结构中:Artist/Album/Track.mp3
。由于我的汽车的 mp3 播放器很糟糕,我希望将所有 mp3 文件都放在闪存驱动器的根目录中,文件名格式为Artist-Album-Track.mp3
.如何将文件从目录复制到闪存驱动器,同时将路径添加到文件名中?
答案1
一个简单的for
循环就可以做到这一点,通过参数扩展来提取各种元素:
for file in */*/*.mp3;
do
artist=${file%%/*}
rest=${file#*/}
album=${rest%%/*}
track=${rest#*/*}
cp -- "$file" /flash/drive/"${artist}-${album}-${track}"
done
第一个扩展删除了从文件末尾到第一个正斜杠的所有内容 - 生成艺术家。
第二次扩展剥离了领导直到第一个正斜杠为止的字符——将艺术家从路径中剥离。
第三次扩展与第一次类似,从剩余路径中剥离文件名,留下专辑。
第四次扩展与第二次类似,剥离了专辑,只留下文件名。
然后我们用破折号将其全部拼凑在一起,并将其 cp 到所需的 /flash/drive 路径。
答案2
使用find
和bash
将文件复制出$mp3dir
to $destdir
:
mp3dir="$HOME/my_music"
destdir="/mnt/my_mp3_player"
find "$mp3dir" -type f -name '*.mp3' -exec bash -c '
mp3dir=$1; destdir=$2; shift 2
for pathname do
dest=${pathname#$mp3dir/}
dest="$destdir/${dest//\//-}"
if [ -f "$dest" ]; then
printf "%s exist, skipping %s\n" "$dest" "$pathname" >&2
else
cp "$pathname" "$dest"
fi
done' bash "$mp3dir" "$destdir" {} +
这会查找$mp3dir
名称以.mp3
.对于这些批次,将执行一个简短的脚本。该脚本的两个参数是$mp3dir
和$destdir
,其余参数是 MP3 文件的路径名。
该脚本从前两个命令行参数中挑选出两个目录名,然后循环遍历其余参数,为$dest
每个 MP3 文件构建目标路径名。这是使用参数替换来完成的,该参数替换在删除路径名的bash
初始位后将路径名中的所有斜杠替换为破折号。$mp3dir
如果$dest
已经存在,则会打印一条有关此文件的消息,否则将复制该文件。
有关的:
答案3
我认为操纵是一种不好的做法多个字符串具有 shell 参数扩展和变量赋值。
root_dir="~/songs"
for fn in */*/*.mp3
do
cp -v "$fn" "${root_dir}/${fn////-}" # replace all slashes by dashes
done
输出:
~/songs/Artist-Album-Track.mp3
答案4
背景
我很好奇是否可以制定一个实现两个目标的解决方案:
- 更容易阅读
- 更容易调试
基于 @kusalananda 对另一个 U&L 问答的精彩回答,标题为:了解 -exec 选项find
,我想我已经想出了一些办法可以解决你的问题。
首先,我的示例目录结构模仿了您的目录结构。
$ mkdir -p Artist{1..5}/Album{1..5}
$ touch Artist{1..5}/Album{1..5}/Track{1..5}.mp3
结果如下:
$ find Artist* -type f | head
Artist1/Album1/Track1.mp3
Artist1/Album1/Track2.mp3
Artist1/Album1/Track3.mp3
Artist1/Album1/Track4.mp3
Artist1/Album1/Track5.mp3
Artist1/Album2/Track1.mp3
Artist1/Album2/Track2.mp3
Artist1/Album2/Track3.mp3
Artist1/Album2/Track4.mp3
Artist1/Album2/Track5.mp3
现在将您的 mp3 文件复制到./targetDir
:
$ find . -type f -name '*.mp3' -exec sh -c \
'cp {} targetDir/$(echo "{}" | sed "s#\./##g;s#/#-#g")' \;
结果是这样的:
$ ls targetDir/ | head
Artist1-Album1-Track1.mp3
Artist1-Album1-Track2.mp3
Artist1-Album1-Track3.mp3
Artist1-Album1-Track4.mp3
Artist1-Album1-Track5.mp3
Artist1-Album2-Track1.mp3
Artist1-Album2-Track2.mp3
Artist1-Album2-Track3.mp3
Artist1-Album2-Track4.mp3
Artist1-Album2-Track5.mp3
我喜欢这种方法,因为我可以先将cp ...
命令包装起来echo
,这样我就可以在提交工作之前验证输出:
$ find . -type f -name '*.mp3' -exec sh -c \
'echo "cp {} targetDir/$(echo "{}" | sed "s#\./##g;s#/#-#g")"' \;
cp ./Artist1/Album1/Track1.mp3 targetDir/Artist1-Album1-Track1.mp3
cp ./Artist1/Album1/Track2.mp3 targetDir/Artist1-Album1-Track2.mp3
cp ./Artist1/Album1/Track3.mp3 targetDir/Artist1-Album1-Track3.mp3
cp ./Artist1/Album1/Track4.mp3 targetDir/Artist1-Album1-Track4.mp3
cp ./Artist1/Album1/Track5.mp3 targetDir/Artist1-Album1-Track5.mp3
cp ./Artist1/Album2/Track1.mp3 targetDir/Artist1-Album2-Track1.mp3
cp ./Artist1/Album2/Track2.mp3 targetDir/Artist1-Album2-Track2.mp3
cp ./Artist1/Album2/Track3.mp3 targetDir/Artist1-Album2-Track3.mp3
cp ./Artist1/Album2/Track4.mp3 targetDir/Artist1-Album2-Track4.mp3
...
怎么运行的
该解决方案获取以下 shell 命令的输出find
,然后运行:
cp {} targetDir/$(echo "{}" | sed "s#\./##g;s#/#-#g")'
这将使cp
文件从Artist../Album../Track..
到targetDir/..
并重新制定名称,以便在有-
正斜杠 ( ) 的地方有破折号 ( ) /
。
笔记:我向sed
.第一个删除如果您使用而不是./
可能存在的任何前缀。find . ...
find Artist* ..
备择方案?
我认为使用find
and来执行此操作cp
仍然不是理想的解决方案。我也维护 MP3 文件的目录,我认为rsync
通过您提供的列表执行类似的操作rsync
可能是一个更有用的实现,从长远来看,因为您可以使用它来更新,而不是每次运行它时重新复制。
$ man rsync
...
--files-from=FILE read list of source-file names from FILE
只是一个想法。