linux - 从文件名中递归删除模式而不更改路径
问题描述
我在目录树中有数千个文件,文件名如下:
/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
如何修改此脚本以使用我描述的模式重命名文件?
解决方案
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
,我们遍历所有这些参数。
推荐阅读
- python - 如何从python中的子字符串中查找给定字符的最大长度?
- python - 在 Python 等价于 PHP 的数组中声明数组
- c# - Unity 在游戏对象之间切换
- php - 切换php版本后未加载libldap-2.4.2.dylib
- html - 无法在 HTML 按钮中使用换行和粗体
- arrays - 使用链表数组作为C中的bin的基数排序
- arrays - 递归 F# 函数中的数组与列表
- c# - 带有 ASP.NET Core MVC 和 DataDTO 的 POST 请求返回 null
- c - 读取系统调用返回垃圾并读取比写入系统调用在套接字中写入的次数更多
- gcc - 通过 cmake 链接 libmodbus