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

ops*_*psb 80 bash scripting replace sed find

我想浏览一堆目录并将所有以_test.rb结尾的文件重命名为_spec.rb结尾.这是我从未想过如何处理bash的事情所以这次我认为我会付出一些努力来实现它.到目前为止,我做得很短,我最大的努力是:

find spec -name "*_test.rb" -exec echo mv {} `echo {} | sed s/test/spec/` \;
Run Code Online (Sandbox Code Playgroud)

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

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

mv original original
Run Code Online (Sandbox Code Playgroud)

即sed的替换已经丢失.有什么诀窍?

ram*_*tam 116

要以最接近原始问题的方式解决它可能会使用xargs"args per command line"选项:

find . -name *_test.rb | sed -e "p;s/test/spec/" | xargs -n2 mv
Run Code Online (Sandbox Code Playgroud)

它以递归方式查找当前工作目录中的文件,回显原始文件名(p),然后是修改后的名称(s/test/spec/),并将其全部输入到mv成对(xargs -n2)中.请注意,在这种情况下,路径本身不应包含字符串test.

  • 不幸的是,这有空白问题.因此,使用名称中包含空格的文件夹将在xargs处将其分解(使用-p确认为详细/交互模式) (8认同)
  • 如果你需要处理路径中的空白而你正在使用GNU sed> = 4.2.2那么你可以使用`-z`选项并找到`-print0`和xargs` -0`:`find -name'*._ test.rb'-print0 | sed -ze"p; s/test/spec /"| xargs -0 -n2 mv` (3认同)

Fre*_*Foo 32

发生这种情况是因为sed接收字符串{}作为输入,可以通过以下方式验证:

find . -exec echo `echo "{}" | sed 's/./foo/g'` \;
Run Code Online (Sandbox Code Playgroud)

foofoo以递归方式打印目录中的每个文件.这种行为的原因是,当它扩展整个命令时,shell会执行一次管道.

没有办法sed以这样的方式引用管道,find它将为每个文件执行它,因为find不通过shell执行命令,并且没有管道或反引号的概念.GNU findutils手册解释了如何通过将管道放在单独的shell脚本中来执行类似的任务:

#!/bin/sh
echo "$1" | sed 's/_test.rb$/_spec.rb/'
Run Code Online (Sandbox Code Playgroud)

(sh -c在一个命令中可能有一些不正常的使用方法和大量的引号来完成所有这些,但我不打算尝试.)

  • 对于那些对sh -c的反常使用感到疑惑的人来说,这是:find spec -name"*_test.rb"-exec sh -c'echo mv"$ 1""$(echo"$ 1"| sed s/test.rb\$/spec.rb /)"'_ {} \; (25认同)

ajr*_*eal 23

你可能想要考虑其他方式

for file in $(find . -name "*_test.rb")
do 
  echo mv $file `echo $file | sed s/_test.rb$/_spec.rb/`
done
Run Code Online (Sandbox Code Playgroud)

  • 如果您的文件名包含空格,则无效.`for`将它们分成单独的单词.您可以通过指示for循环仅在换行符上拆分来使其工作.有关示例,请参见http://www.cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html. (5认同)
  • **for $ file(find.-name"*_test.rb"); 做echo echo mv $ file`echo $ file | sed s/_test.rb $/_ spec.rb /`; 完成**是一个单行,不是吗? (2认同)

csg*_*csg 17

我发现这个更短

find . -name '*_test.rb' -exec bash -c 'echo mv $0 ${0/test.rb/spec.rb}' {} \;
Run Code Online (Sandbox Code Playgroud)


Way*_*rad 9

如果你愿意,你可以在没有sed的情况下完成:

for i in `find -name '*_test.rb'` ; do mv $i ${i%%_test.rb}_spec.rb ; done
Run Code Online (Sandbox Code Playgroud)

${var%%suffix}剥离suffix的价值var.

或者,使用sed来做:

for i in `find -name '*_test.rb'` ; do mv $i `echo $i | sed 's/test/spec/'` ; done
Run Code Online (Sandbox Code Playgroud)


pva*_*erk 9

你提到你正在使用bash你的shell,在这种情况下你实际上并不需要findsed实现批量重命名你之后......

假设您使用的bash是shell:

$ echo $SHELL
/bin/bash
$ _
Run Code Online (Sandbox Code Playgroud)

...并假设您已启用所谓的globstarshell选项:

$ shopt -p globstar
shopt -s globstar
$ _
Run Code Online (Sandbox Code Playgroud)

...最后假设您已经安装了该rename实用程序(在util-linux-ng包中找到)

$ which rename
/usr/bin/rename
$ _
Run Code Online (Sandbox Code Playgroud)

...然后你可以在bash one-liner中实现批量重命名,如下所示:

$ rename _test _spec **/*_test.rb
Run Code Online (Sandbox Code Playgroud)

(globstarshell选项将确保bash找到所有匹配的*_test.rb文件,无论它们嵌套在目录层次结构中有多深......用于help shopt了解如何设置选项)


l3x*_*l3x 6

最简单的方法:

find . -name "*_test.rb" | xargs rename s/_test/_spec/
Run Code Online (Sandbox Code Playgroud)

最快的方法(假设您有4个处理器):

find . -name "*_test.rb" | xargs -P 4 rename s/_test/_spec/
Run Code Online (Sandbox Code Playgroud)

如果要处理大量文件,则通过管道传输到xargs的文件名列表可能会导致生成的命令行超过允许的最大长度.

您可以使用检查系统的限制 getconf ARG_MAX

在大多数Linux系统上,您可以使用free -bcat /proc/meminfo查找您需要使用多少RAM; 否则,请使用top或您的系统活动监控应用程序.

一种更安全的方式(假设您有1000000字节的ram可以使用):

find . -name "*_test.rb" | xargs -s 1000000 rename s/_test/_spec/
Run Code Online (Sandbox Code Playgroud)