在连续数据流上使用grep
1. 简介
Linux 中有多个实用程序,它们可以作用于文件和标准输入。此外,有些甚至可以对定向到他们的连续数据流采取行动。
在本教程中,我们处理grep (全局正则表达式打印)命令以及我们如何:
- 将连续的数据流传递给grep
- 过滤并输出该数据
特别是,我们定义了流并讨论了流操作。之后,我们简要讨论grep如何处理流和文件。接下来,我们看到由特定工具产生的连续流。最后,grep与连续流一起使用,我们还演示了缓冲控制。
我们使用 GNU Bash 5.1.4 在 Debian 11 (Bullseye) 上测试了本教程中的代码。它是 POSIX 兼容的,应该可以在任何这样的环境中工作。
2. streams
在最基本的层面上,streams 只是数据的管道。注意“for”这个词的选择,而不是“with”。这是因为它考虑到流的存在不需要数据这一事实。
考虑一个键盘。该输入设备有一个流,但这并不意味着我们不断地敲击键来为其提供数据。在实践中,感兴趣的进程可以订阅流,就好像它们是广播电台一样。
他们以多种方式做到这一点:
我们可以在进程之间发送信号以通知它们发生事件,但它们通常不会将实际数据与元数据一起保存。另一方面,套接字主要用于网络。实际上,我们可以将套接字大致等同于网络管道。
这导致我们在一般情况下操纵流——我们的主要兴趣。
3. 流操作
无论是通过流重定向 还是直接管道 ,我们都可以修改和转移流。
3.1. 重定向
一般来说,我们有很多重定向的方法:
$ echo 'Data.' >> file.ext
$ cat file.ext
Data.
$ echo 'Data.' | cat
Data.
在上述两种情况下,我们都在处理重定向操作符。首先,我们使用*»将一些数据echo 到文件中。接下来,我们通过cat (Concatenate)将其输出回来。之后,我们使用 | 管道传输相同的信息直接给cat*。
重要的是,管道几乎总是有缓冲区。
3.2. 缓冲
缓冲区的使用通常意味着信息无法通过,除非已经加载了给定的数量以进行传输或一端终止。特别是,可以通过特殊字符终止进程或流。简而言之,我们可以将缓冲区定义为数据临时积累的地方。
一些命令也直接使用缓冲区。
4. grep
grep工具具有内部缓冲功能。它通常在文件操作期间单独运行。另一方面,grep也可以处理流,流本身提供了第二个缓冲层。
4.1. 流
事实上,我们可以通过流重定向将数据传递给grep :
$ echo 'Content.' | grep 'Con'
Content.
在这种情况下,我们直接通过管道处理一个字符串。特别是,我们通过管道重定向stdout。一旦grep处理完字符串,所有进程都会随着 pipe 终止。
但是,还有另一种方法。
4.2. 文件
当然,grep可以直接使用文件名直接作用于文件:
$ echo 'Content.' > file.ext
$ grep 'Con' file.ext
Content.
但是如果我们想监控文件的变化呢?通过与另一个工具相结合,我们可以做到这一点。
*5. tail 的连续流
tail命令有*-f*(跟随)标志,它等待文件更新并将它们添加到输出中,而不是在执行后直接终止。
例如,如果我们在一个终端中开始一个文件的尾随tail并在另一个终端中向该文件发送数据,我们希望在第一个终端中看到相同的数据。让我们看看这个在行动。
首先,我们运行tail:
$ tail -f /file.ext
之后,在另一个终端中,我们将数据发送到file.ext:
$ echo 'Line.' >> /file.ext
事实上,我们在另一端看到了相同的信息。现在让我们将过滤器添加到等式中。
6. 使用grep和tail进行连续流处理
这一次,我们将一个连续的流从tail传递到grep:
$ tail -f /file.ext | grep 'Line'
接下来,我们在另一个终端中将数据添加到文件中:
$ echo 'Line.' >> /file.ext
现在,根据确切的设置,我们可能看不到输出。为什么?因为缓冲。管道和grep缓冲区都可能延迟输出,直到换行或一定数量的字节。
但是,我们可以控制和防止这种情况。
7. 缓冲控制
我们可以使用grep的*–line-buffered*标志来强制在每行终止时刷新其缓冲区,而不是等待具体的字节数:
$ tail -f /file.ext | grep --line-buffered 'Line'
在上述之后,附加到file.ext的每一行都应该产生 output。如果我们不输出换行符,则不会输出:
$ echo -n 'Line.' >> /file.ext
尽管进行了修改,我们仍然可能会遇到管道本身缓冲数据的情况。在这些情况下,我们可以使用一个工具:stdbuf 。
事实上,我们可以在管道上强制执行相同的行缓冲:
$ stdbuf --output=L tail -f /file.ext | grep --line-buffered 'Line'
使用等于L (行)的*–output*选项,我们在管道的两侧都有行缓冲。
实际上,我们可以使用stdbuf来完全去除缓冲。为此,我们将L替换为0(无缓冲):
$ stdbuf --output=0 tail -f /file.ext | grep 'Line'
根据设置,此行应在对file.ext进行任何修改时立即生成输出。