可能的重复:
递归重命名文件和目录
我有一个很大的音乐文件目录,该目录经常随着文件和目录的变化而变化。我的偏好是确保文件和目录名称中不包含空格,因此我将它们全部替换为下划线。
我进入主目录并运行以下命令:
$ find -type d -exec rename 'y/\ /\_/' {} \;
问题是,当有子目录时,这个命令似乎会丢失并且会返回错误。所以如果我有以下目录结构:
...如果我运行该命令,我会收到以下错误:
$ find -type d -exec rename 'y/\ /\_/' {} \;
find: `./Test 02': No such file or directory
find: `./Test 01': No such file or directory
find: `./Test 03': No such file or directory
然后结果是我的目录结构如下所示。请注意,子目录中仍然有空格:
如果我再次运行该命令,我会收到这些错误,即使它看起来可能重命名了有问题的目录:
$ find -type d -exec rename 'y/\ /\_/' {} \;
find: `./Test_01/Test A': No such file or directory
find: `./Test_01/Test C': No such file or directory
find: `./Test_01/Test B': No such file or directory
最后,我再次运行该命令,并且没有收到任何错误,并且所有目录和子目录都按照我想要的方式命名:
显然,当我有多个子目录时,这需要运行命令更多次,这可能会变得乏味。
我怎样才能使这个命令只需要运行一次,它就会一次性重命名所有目录和子目录?
我的最终目标是将其包含在 Bash 脚本中,以便我可以将它与其他类似的内务命令一起运行,因此我需要它不返回错误或需要我提供更多输入。另外,如果有影响的话,我正在运行 Ubuntu 12.04。
答案1
发生的情况如下:
find
找到匹配的目录./Test 02
。find
rename
在该目录上执行命令。rename
重命名Test 02
为Test_02
.find
尝试下降到目录中Test 02
。但它已经不存在了。
解决此问题的最简单方法是告诉find
以相反的方式工作:首先在目录内查找匹配项,然后检查目录本身是否匹配。这就是该-depth
选项的作用。
如果您只添加-depth
,您将遇到另一个问题,即当find
到达时Test 01/Test A
,它会调用rename 'y/\ /\_/' 'Test 01/Test A'
,它会尝试将该目录重命名为Test_01/Test_A
。这将失败,因为没有名为 的目录Test_01
。一个简单的解决方法是使用该-execdir
选项,该选项在目录rename
内部调用Test 01
并Test A
作为参数传递。您可以通过在一批中传递多个参数来加快速度rename
,使用+
而不是;
终止-execdir
命令。
find -depth -name '* *' -type d -execdir rename 'y/ /_/' {} +
或者,使用这个简短但神秘的 zsh 命令:
autoload zmv
zmv -Qw '**/*(/D)' '$1${2// /_}'
这zmv
功能根据模式重命名文件。源模式为,它递归地匹配所有子目录中的**/*
所有文件 ( ) ( )。这*
**/
全局限定符(由选项激活-Q
)表示仅匹配目录 ( /
) 并包含点文件 ( D
)。该-w
选项为源模式中的每个通配符创建反向引用。替换以$1
指定第一个通配符的匹配开始(对于**
,这包括最后一个/
),然后是${2// /_}
,它$2
(匹配的内容*
)被修改为用 替换所有空格字符_
。添加-v
选项来查看命令的作用,您会注意到它像 一样首先遍历深度find -depth
。