复制文件,将路径添加到文件名中

复制文件,将路径添加到文件名中

我想将 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

使用findbash将文件复制出$mp3dirto $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* ..

备择方案?

我认为使用findand来执行此操作cp仍然不是理想的解决方案。我也维护 MP3 文件的目录,我认为rsync通过您提供的列表执行类似的操作rsync可能是一个更有用的实现,从长远来看,因为您可以使用它来更新,而不是每次运行它时重新复制。

$ man rsync
...
        --files-from=FILE       read list of source-file names from FILE

只是一个想法。

相关内容