Contents

使用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

正如我们在上面的输出中看到的,我们有两个应用程序的四个日志文件——app1app2

这些日志文件位于不同的目录中。此外,一些日志文件不区分大小写,包含“安全警报”日志。因此,我们的要求是找到那些包含“安全警报”条目的日志文件,并在一个命令中将它们移动到新目录

今天,我们将讨论实现这一目标的三种方法:

接下来,让我们看看他们的行动。

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. 使用grepxargs命令

我们已经学会了使用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 | whilegrep | xargs

我们已经按照 grep 模式解决了两个解决方案…… * | while或 xargs*。我们可能会问,这两种方法有什么区别?此外,哪个是首选?

首先,*“while”是一个 shell 语句,它是“内置的”,而* xargs命令是findutils 包中的**一个外部命令。因此,xargs并不是普遍可用的,尽管大多数现代 Linux 发行版都默认安装了它。

接下来,我们来说说性能。

显然,while循环会在每一步中读取一个文件并执行 mv命令,直到所有文件都被读取完。另一方面,xargs命令的工作方式不同。** xargs会将找到的文件构建到包中,直到达到系统定义的限制,然后通过命令运行它们**,在这种情况下是mv。换句话说,xargs执行mv 的次数尽可能少。

因此,“grep | while”方法将在我们的示例中执行三个 mv命令。但是“grep | xargsmv只运行一次。也就是说,“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

好的!我们的命令有效。