更干净的脚本,用于递归替换所有目录和文件名中的字符串

更干净的脚本,用于递归替换所有目录和文件名中的字符串

我想以递归方式将所有目录和文件名中的字符串替换为不同的字符串。因此,如果我想用 bar 替换 foo 并拥有此文件:

 foo_project/my_app/old_foo/start_foo.sh

我希望它成为

 bar_project/my_app/old_bar/start_bar.sh

我已经能够做到这一点,但它非常丑陋。从记忆中写下来(不能复制粘贴,所以请原谅拼写错误)我的命令是:

find $PROJECT_DIR -name "*foo*" | tac | xargs -n 1 -I % bash -c "eval mv % \$\( echo % \| sed \''s/\(.*\)foo/\1$PROJECT_NAME/'\' \)"

这对我有用,但很可怕。有没有更干净的方法来做我想做的事?

为了给出上述命令的上下文,我遇到的问题是早期的重命名命令会通过使路径不同来破坏后来的 mv 命令。这就是为什么我在 xargs 之前切换到 tac(而不是我开始使用的 find 的 -evalcmd 参数),以确保我首先运行最深层嵌套的重命名。然后我不得不让我的 sed 只重命名最后的foo 的实例,这样我可以在重命名文件夹之前移动文件夹内的所有内容。

丑陋的部分是需要 eval,因此需要多个级别的转义参数,因为我不知道如何将 xargs 的内容传递给 sed 命令。不过,必须有更干净的东西吗?

答案1

如果您使用的是 CentOS 或某些没有rename(有时称为prename)的系统,那么建议重复是一个值得一看的好地方。如果您使用的是 Debian 派生系统,这也可能适合您:

find "$PROJECT_DIR" -depth -type d -execdir echo rename 's/foo/bar/' {} +

这将下降目录树深度优先并重命名每个目录中的所有项目,在每个实例中更改foo为。bar

(实际上,除了向您展示它将要做什么之外,它什么也不做。echo当您满意时将其删除...)

答案2

对于像这样的潜在棘手和潜在危险的事情,我喜欢生成一个脚本来完成我想要的事情。

$ find foo_project -print > /tmp/list
$ vi /tmp/list
:%s/.*/'&'/            # quote all filenames just to be safe
:%s/.*/mv & &/         # generate mv commands
:%s/' '/'\r'/          # break them for the next step
:v/^mv /s/foo/bar/g    # modify the second arguments
:g/^mv /j              # re-join the lines

现在花点时间检查您刚刚创建的脚本,以确保它将执行您想要执行的操作:

mv 'foo_project/my_app/old_foo/start_foo.sh' 'bar_project/my_app/old_bar/start_bar.sh'
...

如果你喜欢它:

:wq
$ sh /tmp/list

这是我一直使用的通用技术。编辑脚本的确切方式取决于您想要完成的任务。这不是性感的俏皮话,但很安全。

现在,如果这是您想要定期做的事情,而不是一次性的事情,那么这种技术可能不是您想要的。但对于一次性操作(您需要凭感觉完成)来说,这非常有效。

现在,如果您想要一个可重用的解决方案,那么是时候编写一个脚本了:

#!/bin/sh
find foo_project -print | while read filename; do
  newname=`echo $filename | sed -e 's/foo/bar/g'`
  echo "renaming $filename => $newname"
  mv "$filename" "$newname"
done

相关内容