首页 > 解决方案 > 从文件名中递归删除模式而不更改路径

问题描述

我在目录树中有数千个文件,文件名如下:

/Folder 0001 - 0500/0001 - Portrait - House.jpg
/Folder 2500 - 3000/2505 - Landscape - Mountain.jpg

使用 linux 命令行,我想删除文件名中第一个单词之前的所有内容,例如“0001 -”和“2500 -”。新文件名如下所示:

/Folder 0001 - 0500/Portrait - House.jpg
/Folder 2500 - 3000/Landscape - Mountain.jpg

我修改了一个有效的脚本:

find . -type f -name "*-*" -exec bash -c 'f="$1"; g="${f/[[:digit:]]/ -/ /}"; echo mv -- "$f" "$g"' _ '{}' \;

这里的问题是它会删除部分路径而不是文件名,因此实际输出会生成如下文件名:

/Folder  -/ /001 - 0500/0001 - Portrait - House.jpg
/Folder  -/ /500 - 3000/2505 - Landscape - Mountain.jpg

如何修改此脚本以使用我描述的模式重命名文件?

标签: linuxbash

解决方案


find . -mindepth 2 -type f -name "*-*" -exec bash -c '
  shopt -s extglob
  for arg do
    dir=${arg%/*}
    basename_old=${arg##*/}
    basename_new=${basename_old##+([[:digit:]]) - }
    [[ "$basename_new" = "$basename_old" ]] && continue   # skip when no rename needed
    printf "%q " mv -- "$dir/$basename_old" "$dir/$basename_new"
    printf "\n"
  done
' _ {} +

您可以在https://ideone.com/YJNL9c看到此代码运行


  • 使用参数扩展将目录名从文件名中分离出来,可以单独操作它们。
    • ${arg%/*}/从变量 in 中删除最后一个之后的所有内容arg-- 从而删除文件名,离开目录,当路径至少有一个目录段时(提供这种保证是 的原因-mindepth 2
    • ${arg##*/}从头开始删除最长的匹配*/项 - 从而删除目录,留下基本文件名。
  • 通过启用shell 选项,我们在 fnmatch/glob 样式的表达式中获得了类似正则表达式的功能,包括匹配一个或多个单个数字的能力extglob这就是为什么+([[:digit:]]) -评估为“一位或多位数字,后跟-”的原因。
  • 通过在生成 shell 命令时使用printf '%q '而不是,echo即使不控制我们的文件名,我们也会生成安全引用的输出。
  • 通过使用-exec ... {} +,我们将多个参数传递给每个bash实例,而不是为找到的每个文件调用单独的解释器。使用for arg do,我们遍历所有这些参数。

推荐阅读