使用grep查找文件中的内容并移动匹配的文件
1. 概述
我们知道grep 命令是在 Linux 命令行中用于文件内容搜索的便捷实用程序。此外,grep允许我们递归搜索目录 以查找目录中与给定模式匹配的所有文件。
在本教程中,让我们探索如何使用grep搜索文件并将找到的文件一次性移动到目录中。
2. 问题介绍
假设一些文件位于不同的子目录中。首先,我们将递归地在父目录的所有文件中搜索模式。然后,我们想将匹配的文件移动到一个新的地方。
由于文件位于不同的目录中,我们应该注意找到的文件可能具有相同的文件名,例如*/parent/dir1/file.txt和/parent/dir2/file.txt*。
在本教程中,我们假设找到的文件没有重复的文件名,因为我们的目标是解决解决一般问题的方法。但是,在文件名冲突的情况下,我们可能对处理冲突有特定的要求,例如覆盖或重命名。
通常,一个例子可以快速解释一个问题。我们先看一下 logs目录下的一些日志文件:
$ head logs/**/*.log
==> logs/app1/app1.log <==
2022-01-20 15:21:10 application started
2022-01-20 17:07:14 [Warn] Security alert: 10 Permission Denied Requests from the same IP ...
2022-01-20 17:08:14 [Warn] High RAM usage: 90%
2022-01-20 17:14:10 RAM usage is back to normal
==> logs/app1/app1_user.log <==
2022-01-20 19:22:10 user Kevin created
2022-01-20 20:21:10 user Kevin login
2022-01-20 22:18:10 security alert: 10 times failed login from the same IP ...
==> logs/app2/app2.log <==
2021-11-20 15:21:10 application started
2021-11-20 17:08:14 [Warn] High CPU usage: 80%
2021-11-20 17:14:10 CPU usage is back to normal
==> logs/app2/app2_user.log <==
2021-11-20 19:21:10 user Eric login
2021-11-20 22:08:14 security alert: 10 times failed login from the same IP ...
2021-11-20 23:44:10 user Eric logout
正如我们在上面的输出中看到的,我们有两个应用程序的四个日志文件——app1和app2。
这些日志文件位于不同的目录中。此外,一些日志文件不区分大小写,包含“安全警报”日志。因此,我们的要求是找到那些包含“安全警报”条目的日志文件,并在一个命令中将它们移动到新目录。
今天,我们将讨论实现这一目标的三种方法:
接下来,让我们看看他们的行动。
3. 使用grep命令和while循环
首先,我们检查日志文件的内容以找到要移动的文件。grep命令是此任务的不错选择:
$ grep -lir 'security alert' logs
logs/app2/app2_user.log
logs/app1/app1_user.log
logs/app1/app1.log
正如grep的输出所示,我们找到了三个包含“安全警报”的日志文件。在这里,我们使用了grep的三个选项:
- -l:仅输出匹配的文件名
- -i:不区分大小写的匹配
- -r:递归读取每个目录下的所有文件
在下一步中,while循环将接管控制权。它将从grep的结果中读取每个文件名并将文件移动到目标目录。当然,在我们移动文件之前,我们应该创建目标目录:
$ mkdir logs/security_logs_loop && grep -lir 'security alert' logs | while read log; do mv "$log" logs/security_logs_loop; done
$ tree logs
logs
├── app1
├── app2
│ └── app2.log
└── security_logs_loop
├── app1.log
├── app1_user.log
└── app2_user.log
3 directories, 4 files
正如我们在上面的树输出中看到的,在命令执行之后,三个日志文件已经被移动到了目标目录。
4. 使用grep和xargs命令
我们已经学会了使用grep来获取我们想要移动的文件列表。然后,我们将文件列表通过管道传送到while循环来解决问题。
或者,我们可以将grep的输出通过管道传递给 xargs命令来完成这项工作:
$ mkdir logs/security_logs_xargs && grep -lir 'security alert' logs | xargs -I'{}' mv '{}' logs/security_logs_xargs
$ tree logs
logs
├── app1
├── app2
│ └── app2.log
└── security_logs_xargs
├── app1.log
├── app1_user.log
└── app2_user.log
3 directories, 4 files
上面的输出显示我们已将所需的日志文件移动到目标目录。
在xargs命令中,**我们使用 -I{}占位符来构建 mv FOUND_LOGS TARGET_DIR 命令。
由于 mv 命令支持*-t*选项,我们可以将命令写成更短的形式:
mkdir logs/security_logs_xargs && grep -lir 'security alert' logs | xargs mv -t logs/security_logs_xargs
5. grep | while 与 grep | xargs
我们已经按照 grep 模式解决了两个解决方案…… * | while或 xargs*。我们可能会问,这两种方法有什么区别?此外,哪个是首选?
首先,*“while”是一个 shell 语句,它是“内置的”,而* xargs命令是findutils 包中的**一个外部命令。因此,xargs并不是普遍可用的,尽管大多数现代 Linux 发行版都默认安装了它。
接下来,我们来说说性能。
显然,while循环会在每一步中读取一个文件并执行 mv命令,直到所有文件都被读取完。另一方面,xargs命令的工作方式不同。** xargs会将找到的文件构建到包中,直到达到系统定义的限制,然后通过命令运行它们**,在这种情况下是mv。换句话说,xargs执行mv 的次数尽可能少。
因此,“grep | while”方法将在我们的示例中执行三个 mv命令。但是“grep | xargs ” mv只运行一次。也就是说,“grep | xargs”比“grep | while”,尤其是在移动许多文件时。
6. 使用命令替换
到目前为止,我们已经学习了两种解决问题的方法。但是,如果我们仔细研究这两种解决方案,它们都从grep命令 开始,并将grep的输出传递到 while循环或 xargs。
或者,我们可以将问题解释为:
mv [Required_Files] targetDir
现在,问题转化为如何找到需要的文件并填写上面的命令。找到这些文件对我们来说不是挑战。我们已经使用grep命令完成了几次。
此外,命令替换使我们能够快速获取命令的输出并将其用于进一步的操作。
接下来,让我们尝试使用命令替换来解决问题:
$ mkdir logs/security_logs_cmdsub && mv $(grep -lir 'security alert' logs) logs/security_logs_cmdsub
$ tree logs
logs
├── app1
├── app2
│ └── app2.log
└── security_logs_cmdsub
├── app1.log
├── app1_user.log
└── app2_user.log
3 directories, 4 files
好的!我们的命令有效。