递归重命名文件夹的Bash脚本

5

我想重命名所有文件夹及其内部的文件夹,使文件夹名称中所有下划线都替换为空格。您能帮我吗?

3个回答

7

使用-execdir的答案更简单,但只适用于支持-execdirfind版本,如GNU find,因为POSIX仅指定-exec

如果您只想使用bash,这似乎非常棘手,因为您要重命名find正在搜索的目录。即使通过使用-depth选项对find进行排序,您也需要确保每个-exec仅重新编写路径的最后一个组件。以下是Bash FAQ中给出的示例之一的微小变化,对我来说似乎工作正常:

find . -depth -name "*_*" -exec bash -c 'dir=${1%/*} base=${1##*/}; mv "$1" "$dir/${base//_/ }"' _ {} \;

那个常见问题解答中有更多关于递归命名文件夹的讨论,可能会引起兴趣。
< p > < em > 更新 :由于这个一行代码相当复杂,为了解释起见,将其分解一下可能会更有价值。基本上,< code > find 命令是:
find . -depth -name "*_*" -exec bash -c [SOME-STUFF] _ {} \;

换句话说,查找所有包含下划线的目录,并对于每个这样的目录,从最深处开始运行bash脚本[SOME-STUFF],将参数0设置为_(表示我们不关心它),将参数1设置为find找到的文件名。(find会在-exec之后用文件名替换{}\;只是终止-exec运行的命令。)

然后[SOME-STUFF]部分由以下内容组成:

dir=${1%/*}

...这部分使用参数展开,将从$1(文件名)的结尾删除最短匹配的/*,并将dir设置为结果。同样,这部分内容:

base=${1##*/}

该函数从$1的开头删除最长匹配的*/并将base设置为结果。 因此,base只是路径的最后一个组成部分。

然后重命名实际上是由mv命令完成的,其格式如下:

mv "$1" "$dir/${base//_/ }"

那又一次使用参数展开,这次是用 ${parameter/pattern/string} 语法。文件名($1)被重命名为 $dir 加上 $base,但是在 $base 中的每个下划线都被替换成一个空格。

如果您希望替换值(在此情况下为空格)是来自调用脚本的变量,该怎么办?在示例中,我包含了$SOMEVAR,但不起作用。find . -depth -name "_" -exec bash -c 'dir=${1%/} base=${1##/}; mv "$1" "$dir/${base//_/$SOMEVAR}"' _ {} ; - Jeremy Lyman

1

是的,只需cd到/dir并执行以下操作:

find -depth -type d -execdir rename 's/_/ /g' {} \;

根据发行版的不同,您需要使用perl重命名(有时是prename)。
如果
file $(type -p rename)

输出包含ELF,看起来不太好 ;)

编辑 添加了-depth和-execdir选项


1
这是行不通的,因为在输出find找到'foo_bar/baz_quux'之前,'foo_bar'就会被找到并重命名,当尝试重命名后者时,它将无法找到。 - Mark Longair
没什么大不了的,你可以在find命令后面加上“-depth”选项。 - Michael Krelin - hacker
恐怕仍然不行 - 在该示例中要调用的第一条命令是rename 's/_/ /' ./foo_bar/bar_baz,它会给出错误Can't rename ./foo_bar/bar_baz ./foo bar/bar_baz: No such file or directory - 如果您正在使用rename,则需要一个正则表达式,它只会替换路径中最后一个组件中的下划线。 - Mark Longair
尝试使用-execdir而不是-exec - Manny D
1
另一个错误是这只会替换目录名中的第一个下划线 - 请尝试使用s/_/ /g - Mark Longair

0
如果您有bash4,可以运行:
for i in **; do [[ -d "$i" ]] || continue; mv "$i" "${i/_/ }"; done

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接