递归目录中的快速字符串替换

递归目录中的快速字符串替换

我怎样才能做一个快速地文本代替递归的目录和文件名空间单引号?最好使用标准 UNIX 工具,或者众所周知的软件包。

对于许多文件来说,使用速度find非常慢,因为它会为每个文件生成一个新进程,因此我正在寻找一种将目录遍历和字符串替换集成为一个操作的方法。

缓慢搜索:

find . -name '*.txt'  -exec grep foo {} \;

快速搜索:

grep -lr --include=*.txt foo

缓慢替换:

find . -name '*.txt' -exec perl -i -pe 's/foo/bar/' {} \;

快速更换:

# Your suggestion here

(这个速度相当快,但需要两次传递并且不处理空格。)

perl -p -i -e 's/foo/bar/g' `grep -lr --include=*.txt foo`

答案1

你只想使用:

 find . -name '*.txt'  -exec cmd {} \;

cmd那些只能接受一个参数的 s的形式。情况并非如此grep。和grep

 find . -name '*.txt'  -exec grep foo /dev/null {} +

(或-H与 GNU 一起使用grep)。更多相关信息,请访问递归 grep 与 find / -type f -exec grep {} \;哪个更有效/更快?

现在对于替换,这是一样的,perl -pi可以采用多个参数:

 find . -name '*.txt' -type f -exec perl -pi -e s/foo/bar/g {} +

现在,无论文件是否包含,都会重写这些文件foo。相反,您可能想要(假设 GNUgrepxargs/或兼容):

 find . -name '*.txt' -type f -exec grep -l --null foo {} + |
   xargs -r0 perl -pi -e s/foo/bar/g

或者:

 grep -lr --null --include='*.txt' foo . |
   xargs -r0 perl -pi -e s/foo/bar/g

所以只有包含的文件foo被重写。


顺便说一句,--include=*.txt--include作为另一个 GNU 扩展)是一个 shell glob,所以应该被引用。例如,如果--include=foo.txt当前目录中有一个文件被调用,shell 将--include=*.txt在调用之前扩展到该文件grep。如果没有,对于许多 shell,您会收到有关 glob 无法匹配任何文件的错误。

所以你会想要grep --include='*.txt'

答案2

当您的find表达式如此简单时,您可以使用 shell 来进行通配。您可能遇到的主要限制是处理的文件数量超出了命令行所能容纳的数量。

bash 中的一个例子:

$ shopt -s globstar

$ date > a.txt
$ date > b.txt
$ date > c.txt
$ cat *.txt
Thu Mar 29 14:57:57 EDT 2018
Thu Mar 29 14:58:00 EDT 2018
Thu Mar 29 14:58:02 EDT 2018
$ mkdir -p deep/sub/dir
$ mv *.txt deep/sub/dir

$ perl -pi -e 's/EDT/EST/' **/*.txt

$ cat deep/sub/dir/*.txt
Thu Mar 29 14:57:57 EST 2018
Thu Mar 29 14:58:00 EST 2018
Thu Mar 29 14:58:02 EST 2018

答案3

您可以使用find ... -exec“+”终止符而不是“;”批量运行文件而不是一次运行一个文件(前提是正在exec运行的命令支持一次调用多个文件):

find . -name '*.txt' -exec grep foo {} +
find . -name '*.txt' -exec perl -i -pe 's/foo/bar/' {} +

相关内容