递归地从文件名中删除模式而不更改路径

时间:2018-04-28 16:59:05

标签: linux bash

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

/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

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

1 个答案:

答案 0 :(得分:2)

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

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