为什么是头;大文件上的 tail 有时需要很长时间,有时则不需要?

为什么是头;大文件上的 tail 有时需要很长时间,有时则不需要?

我正在编写一个 bash 脚本,该脚本读取 .txt 文件的前 10 行和最后 10 行。它查找开始(头)和完成(尾)并使用 grep 比较出现的次数。这些文件非常大,这就是为什么我选择只读取文件的头部和尾部而不是整个文本。但是,当我运行脚本时,大文件需要很长时间才能“完成”(其中包括读取前 10 行和后 10 行以及比较,这项任务只需要一两分钟)。

在观看脚本输出文本时,我注意到这个问题。因此,我决定看看当我直接从命令行执行 head/tail (加上 grep,以模拟脚本中执行的内容)命令时是否需要类似的时间。令人惊讶的是,命令几乎立即执行。我觉得这很奇怪,然后我再次运行了脚本。这一次,脚本会尖叫着穿过之前卡住的文件,直到到达下一个我尚未运行 head/tail/grep 命令的“大”文件。

这让我开始思考,bash 是否会像缓存一样存储命令的结果?另外,可能导致这些命令的原因是:

 head -n 10 /file/path/myfile.txt | grep -w -c 'lead word'
 tail -n 10 /file/path/myfile.txt | grep -w -c 'end word'

需要这么长时间才能执行?

编辑:我相信上面的头/尾线是问题根源的原因是因为有回声线应该在头/尾单独完成时打印。我检查了文件的行大小,它们并不比几分钟内完成的文件长很多。

有人可以给我更详细的解释,说明头/尾在技术层面上是如何工作的吗?我对文件的“前 x 行/后 x 行”有非常基本的了解。

答案1

不,bash 不会缓存命令的输出,因为输出可能会在不同的运行中发生变化; bash 没有可靠的方法来跟踪文件是否已被另一个进程修改,这非常重要,因此bash可以判断其缓存结果是否仍然有效。

然而,这里还有其他事情在起作用。当您使用 shell(例如bash)时,您会同时与系统的多个部分进行交互。例如:

  • 外壳本身
  • GNU Readline,这是多个 shell 和其他工具使用的行编辑界面
  • libc实现有时会在几乎任何程序中导致令人困惑的行为。
  • 终端本身可能有奇怪的行为,并且实际上可以响应自己的命令。 (例如,Backspack和中的任何一个Delete可能不可用,或者它们可以互换)
  • 终端所在的 GUI 窗口(如果适用)。例如,它可能有一个Ctrl它允许使用特殊的按键序列(例如, ++后跟数字)Shift在终端中输入 Unicode 字符。u
  • 内核,包括其所有模块和驱动程序
  • 硬件本身当然可能会过热、短路、断电等

在这种情况下,我想说最大的贡献者不是bash它本身,而是内核中实现的文件系统级缓存机制。一旦你读取了一个文件一次,它的很大一部分就会最终进入文件系统缓存,这是为此目的保留的一大块内存。

当第二次对该文件进行操作时,shell不会再次触发物理硬件的读取,而是从缓存中检索文件内容。与磁盘读取相比,您在 bash 中(重新)执行的几乎任何操作都将非常快。这就是为什么您没有注意到 bash 实际上正在重新执行命令,因为慢速磁盘读取丢失了。

相关内容