使用find和sed递归重命名文件

我想浏览一堆目录,然后将以_test.rb结尾的所有文件重命名为以_spec.rb结尾的文件。这是我从来没有想过如何使用bash的东西,所以这次我想我会花些力气将它钉牢。不过到目前为止,我的最大努力是:


find spec -name "*_test.rb" -exec echo mv {} `echo {} | sed s/test/spec/` \;

注意:exec之后有一个额外的回显,以便在测试时打印命令而不是运行命令。


当我运行它时,每个匹配文件名的输出是:


mv original original

即用sed替代已丢失。诀窍是什么?


慕勒3428872
浏览 1093回答 3
3回答

慕姐4208626

发生这种情况是因为sed接收到该字符串{}作为输入,可以使用以下命令进行验证:find . -exec echo `echo "{}" | sed 's/./foo/g'` \;foofoo递归地为目录中的每个文件打印。出现这种情况的原因是,当管道扩展整个命令时,管道将由外壳执行一次。由于无法通过外壳执行命令并且没有管道或反引号的概念,因此无法以对每个文件都执行sed管道的方式来引用管道。GNU findutils手册介绍了如何通过将管道放置在单独的shell脚本中来执行类似的任务:findfind#!/bin/shecho "$1" | sed 's/_test.rb$/_spec.rb/'(sh -c在一个命令中可能有一些错误的使用方式和大量的引号来完成所有这些操作,但是我不会尝试。)

慕慕森

要以最接近原始问题的方式解决它,可能要使用xargs“每个命令行的args”选项:find . -name *_test.rb | sed -e "p;s/test/spec/" | xargs -n2 mv它递归地在当前工作目录中查找文件,回显原始文件名(p),然后回显修改的名称(s/test/spec/),并将它们全部mv成对地馈入(xargs -n2)。注意在这种情况下路径本身不应包含字符串test。

慕尼黑的夜晚无繁华

您提到您正在bash用作外壳,在这种情况下,您实际上并不需要使用find它sed来实现要重命名的批处理...假设您将其bash用作外壳程序:$ echo $SHELL/bin/bash$ _...并假设您启用了所谓的globstarshell选项:$ shopt -p globstarshopt -s globstar$ _...最后假设您已经安装了该rename实用程序(可在util-linux-ng软件包中找到)$ which rename/usr/bin/rename$ _...然后您可以按以下步骤在bash一线式中实现批量重命名:$ rename _test _spec **/*_test.rb(globstarshell选项将确保bash能够找到所有匹配的*_test.rb文件,无论它们在目录层次结构中的嵌套深度如何...用于help shopt了解如何设置该选项)
打开App,查看更多内容
随时随地看视频慕课网APP