递归搜索并替换文本文件
1. 概述
在文件中搜索模式并将其替换为新文本是我们在 Linux 命令行中工作时的典型操作。
有时,我们希望搜索并替换给定目录下的所有文本文件,包括其子目录下的文本文件。
在本教程中,我们将通过示例讨论如何在文本文件中递归搜索和替换。
2. 例子
在我们进入解决方案的细节之前,让我们创建一个名为myDir的示例目录,并在其下创建一些子目录和文件:
$ tree myDir
myDir
├── dir1
│ ├── dir1.1
│ │ ├── dir1.1.1
│ │ │ └── text1.1.1.txt
│ │ └── text1.1.txt
│ └── text1.txt
└── parent.txt
让我们看一下每个文本文件的内容:
$ head $(find myDir -name "*.txt")
==> myDir/dir1/dir1.1/dir1.1.1/text1.1.1.txt <==
text1.1.1: I like Linux.
==> myDir/dir1/dir1.1/text1.1.txt <==
text1.1: I like Linux.
==> myDir/dir1/text1.txt <==
text1: I like Linux.
==> myDir/parent.txt <==
Parent: I like Linux.
接下来,**我们要把myDir目录下的所有文本文件中的 “ Linux”替换为“ Linux operating system ” 。
3. 分而治之
我们并没有真正面临算法问题。但是,我们可以借用“分而治之 ”的思想来解决。
我们可以将问题分为两个子问题:
- 在单个文件中将“ Linux ”替换为“ Linux 操作系统”
- 查找给定目录myDir下的所有文本文件
我们将分别解决这两个子问题,然后将它们组合起来解决我们原来的问题。
接下来,让我们看看如何分而治之。
4. 在单个文本文件中搜索和替换
首先,让我们解决问题:在单个文件中将“ Linux ”替换为“ Linux operating system ”。
在 Linux 命令行中,我们可以通过多种方式进行文本替换并将结果保存回文件。
为了解决这个问题,我们将选择sed 命令来执行搜索和替换工作。
让我们看看一个简单的sed命令如何在parent.txt中将“ *Linux”*替换为“ Linux operating system ” :
$ sed -i 's/Linux/& operating system/g' parent.txt
$ cat parent.txt
Parent: I like Linux operating system.
接下来,我们需要找到myDir目录下的所有文本文件,并将找到的文件传递给上面的**sed命令来解决问题。
5.递归搜索和替换
有很多方法可以找到给定目录下的所有文本文件,并对找到的文件调用sed命令。
在本节中,我们将介绍四种不同的方法。
5.1. 使用find命令和 *-exec command {} +*选项
find 命令 可以递归地查找给定目录下的文件。此外,它提供了一个选项“ *-exec command {} +”*来对所有找到的文件执行命令。
让我们组装我们的 sed命令和一个find命令来解决我们的问题:
$ find myDir -name '*.txt' -exec sed -i 's/Linux/& operating system/g' {} +
在上面的命令中,**“ {} ”是一个占位符,将由所有找到的文件填充。**因此, sed命令将如下所示:
$ sed -i '..code..' foundFile1 foundFile2 foundFile3...foundFileN
这样,我们只调用 一次sed命令而不是n次。
现在,让我们检查目录 myDir下的所有文本文件是否都已更改:
$ head $(find myDir -name "*.txt")
==> myDir/parent.txt <==
Parent: I like Linux operating system.
==> myDir/dir1/text1.txt <==
text1: I like Linux operating system.
==> myDir/dir1/dir1.1/text1.1.txt <==
text1.1: I like Linux operating system.
==> myDir/dir1/dir1.1/dir1.1.1/text1.1.1.txt <==
text1.1.1: I like Linux operating system.
5.2. 使用find命令和xargs命令
在现实世界中,我们经常看到find命令和 /xargs 命令一起工作。
xargs命令可以读取find命令的输出,这是找到的文件列表,然后将它们构建到另一个命令的参数中。
让我们看看如何结合这两个命令来解决我们的问题:
$ find myDir -name '*.txt' | xargs sed -i 's/Linux/& operating system/g'
我们执行上面的命令后,myDir目录下的所有文本文件都会被递归修改。
5.3. 使用grep命令和xargs命令
顾名思义,find命令可以查找文件。加上-rl选项,grep 命令也可以做到。
这里,-R 选项告诉grep递归搜索目录,而*-l选项是跳过匹配信息,告诉grep*只打印匹配文件的文件名。
让我们看看如何使用grep命令查找所有包含模式“ Linux ”的文件:
$ grep -Rl 'Linux' myDir
myDir/parent.txt
myDir/dir1/text1.txt
myDir/dir1/dir1.1/text1.1.txt
myDir/dir1/dir1.1/dir1.1.1/text1.1.1.txt
现在,为了解决我们的问题,我们只需将此结果通过管道传递给 xargs命令:
$ grep -Rl 'Linux' myDir | xargs sed -i 's/Linux/& operating system/g'
在myDir目录下的所有文本文件中,所有出现的“ Linux ”再次被替换为“ Linux operating system ” 。
5.4. 使用 Zsh Glob (**)
Zsh 是一个功能强大且流行的 shell。zsh glob 支持双星号 (**) glob 来匹配当前目录及其所有子目录下的文件。
让我们看看如何 使用 Zsh递归列出myDir目录下的所有文本文件:
(zsh)$ ls -1 myDir/**/*.txt
myDir/dir1/dir1.1/dir1.1.1/text1.1.1.txt
myDir/dir1/dir1.1/text1.1.txt
myDir/dir1/text1.txt
myDir/parent.txt
因此,我们可以使用 Zsh 更简单地解决我们的问题:
(zsh)$ sed -i 's/Linux/& operating system/g' myDir/**/*.txt
我们看到只用sed命令就可以解决问题。
同样,我们可以使用相同的 glob 检查是否所有文本文件中出现的所有“ Linux ”都被替换:
(zsh)$ head myDir/**/*.txt
==> myDir/parent.txt <==
Parent: I like Linux operating system.
==> myDir/dir1/text1.txt <==
text1: I like Linux operating system.
==> myDir/dir1/dir1.1/text1.1.txt <==
text1.1: I like Linux operating system.
==> myDir/dir1/dir1.1/dir1.1.1/text1.1.1.txt <==
text1.1.1: I like Linux operating system.