【问题标题】:Bash script to rename folders recursively用于递归重命名文件夹的 Bash 脚本
【发布时间】:2023-06-20 10:06:02
【问题描述】:

我想重命名我的所有文件夹和这些文件夹中的文件夹,以便文件夹名称中的所有下划线都替换为空格。你能帮我解决这个问题吗?

【问题讨论】:

    标签: bash scripting recursion rename


    【解决方案1】:

    是的,只需 cd 到 /dir 和 :

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

    根据发行版的不同,您需要 perl 重命名(有时是前缀)。

    如果

    file $(type -p rename)
    

    输出包含 ELF,看起来很糟糕 ;)

    编辑 -depth 和 -execdir 添加了

    【讨论】:

    • 这不起作用,因为在 find 输出 'foo_bar/baz_quux' 之前将找到并重命名 'foo_bar' - 尝试重命名时将找不到后者。
    • 没什么大不了的,可以加-depth切换查找。
    • 恐怕它仍然不起作用 - 在该示例中要调用的第一个命令是 rename 's/_/ /' ./foo_bar/bar_baz,它会给出错误 Can't rename ./foo_bar/bar_baz ./foo bar/bar_baz: No such file or directory - 如果您使用的是 rename你需要一个正则表达式,它只会替换路径最后一个组件中的下划线。
    • 尝试使用-execdir 而不是-exec
    • 另一个错误是这只会替换目录名称中的第一个下划线 - 请尝试s/_/ /g
    【解决方案2】:

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

    如果您只想使用 bash,那么要正确执行此操作非常棘手,因为您正在重命名 find 正在搜索的目录。即使你得到了正确的顺序,通过使用find-depth 选项,你需要确保只用每个-exec 重写路径的最后一个组件。以下是one of the examples given in the Bash FAQ 的一个简单变体,对我来说似乎没问题:

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

    该常见问题解答对递归命名文件夹的问题进行了更多讨论,这可能会引起人们的兴趣。


    更新:由于那条线相当复杂,为了便于解释,可能值得将其分解一下。本质上,find 命令是:

    find . -depth -name "*_*" -exec bash -c [SOME-STUFF] _ {} \;
    

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

    那么[SOME-STUFF]部分是由:

    dir=${1%/*}
    

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

    base=${1##*/}
    

    ... 从$1 的开头删除*/ 的最长匹配并将base 设置为结果。所以base 只是路径的最后一个组成部分。

    那么重命名实际上是通过mv命令完成的,即:

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

    这再次使用参数扩展,这次使用${parameter/pattern/string} 语法。文件名 ($1) 重命名为 $dir,后跟 $base,但 $base 中的每个下划线都替换为空格。

    【讨论】:

    • 如果您希望替换值(在本例中为空格)是源自调用脚本的变量怎么办?在前。我已经包含 $SOMEVAR 不起作用。找 。 -depth -name "_" -exec bash -c 'dir=${1%/*} base=${1##*/}; mv "$1" "$dir/${base//_/$SOMEVAR}"' _ {} \;
    【解决方案3】:

    如果你有 bash4,你可以运行:

    for i in **; do [[ -d "$i" ]] || continue; mv "$i" "${i/_/ }"; done
    

    【讨论】:

      最近更新 更多