【问题标题】:How do I tell tail -f it has finished - cleanly?我如何干净地告诉 tail -f 它已经完成了?
【发布时间】:2014-11-18 00:42:07
【问题描述】:

我正在将一个正在创建的 LOGFILE 复制到远程服务器。

tail -f LOGILE | gzip -c >> /faraway/log.gz

然而,当原来的 LOGFILE 被关闭,并移动到一个存储目录时,我的 tail -f 似乎得到了一些奇怪的数据。

如何确保 tail -f 完全停止并且压缩文件 /faraway/log.gz 是 LOGFILE 的真实副本?

编辑 1

我做了更多的挖掘。

/faraway/log.gz 严重终止 - 在 FIX 消息中途终止。这一定是因为我 ctrlCed 上面的整个管道命令。

如果忽略最后一行,那么原始 LOGFILE 和 log.gz 完全匹配!这是跨大西洋传输的 40G 文件。

我对此印象深刻,因为它完全符合我的要求。有没有读者认为我在这种情况下只是“幸运”——这可能不会在未来起作用吗?

现在,我只需要彻底关闭 gzip。也许按照下面的建议向尾部 PID 发送 kill -9 可能确实允许 GZIP 正确完成其压缩。

【问题讨论】:

  • 你能为这种情况定义“奇怪的数据”吗?另外 LOGFILE 是如何“关闭”的? tail -f 将继续从文件中读取,即使您 mv 它也是如此。您需要给tail 一些停止关注该文件的理由。
  • 如果您想要一个完整且完整的文件副本,tail -f 是适合这项工作的错误工具,一劳永逸。除非您保证在开始该过程时它是空的,否则您甚至都可能赶上开始 - 除非您的 gunzip 实现是在期望压缩流重新启动的情况下构建的,你也会在那里得到惊喜。
  • @EtanReisner 似乎缺少一两行。一旦 LOGFILE 被移动,我就用 ctrl-c 终止该进程。我需要发送 tail -f 一个信号来说明 LOGFILE 已完成 - 让 gzip 特写。
  • @ManInMoon,它似乎只能正常工作,因为您也没有测试过其他极端情况。存在用于远程日志记录的专用工具。使用它们。
  • 如果您可以控制所有修改/访问/删除文件的程序,我相信tail 可以达到您的目的。您的描述更清楚,但需要更多说明。也许您可以将您的目标与此类似:程序 A @机器 A 写入日志文件 X,程序 B @机器 A 读取日志文件 X,等等。他们将以什么顺序写入/读取/删除/移动,@ 987654328@ 命令适合,在序列中,以及在哪个程序中......(如tail 在程序 B @ 主机 B 中)......等等。而不是说“我正在复制......”,我更喜欢“程序 A 正在复制...”

标签: bash unix cygwin gzip tail


【解决方案1】:

要获得完整副本,请使用

tail -n +1 -f your file

如果你不使用-n +1 选项,你只会得到文件的尾部。

但这并不能解决删除/移动文件的问题。实际上,删除/移动文件的问题是IPC(进程间通信)问题,或进程间协作问题。如果您没有其他进程的正确行为模型,则无法解决问题。

例如,如果其他程序将日志文件复制到其他地方,然后删除当前文件,然后程序将输出记录到新的日志文件中......显然您的尾巴无法读取这些输出。

一个值得一提的unix(和类unix系统)的相关特性:

当一个文件被进程A打开读取,但随后被进程A删除时 进程B,物理内容不会立即删除, 因为它的引用计数不为零(有人仍在使用它,即 过程 A)。进程 A 仍然可以访问该文件,直到它关闭 文件。移动文件是另一个问题:如果进程 B 移动 文件到同一个物理文件系统(注意:你可能有很多 附加在系统上的物理文件系统),进程 A 仍然可以 访问文件,即使文件正在增长。这种移动是 只是为了更改名称(路径名+文件名),仅此而已。这 文件的身份(在 unix 中也称为“i-node”)不会改变。然而 如果文件被移动到另一个物理文件系统,本地或远程, 就好像文件被复制然后删除。所以删除规则 提到的可以申请。

您提到的缺失行问题很有趣,可能需要对生成和移动/删除日志文件的程序/进程的行为进行更多分析。

--更新--

很高兴看到您取得了一些进展。就像我说的,像tail 这样的进程在之后仍然可以访问数据 该文件在类 Unix 系统中被删除。

你可以使用 ( echo $BASHPID > /tmp/PID_tail; exec tail -n + 1 -f yourLogFile ) | gzip -c -> yourZipFile.gz

压缩你的日志文件,并通过

杀死tail程序
kill -TERM `cat /tmp/PID_tail`

gzip 应该自行完成而不会出错。即使您担心gzip 会收到损坏 管道信号,您可以使用这种替代方式来防止管道损坏:

 (  ( echo $BASHPID > /tmp/PID_tail; exec tail -n + 1 -f yourLogFile ) ; true ) | gzip -c - > yourZipFile.gz

损坏的管道受到true 的保护,它不打印任何内容,但会自行结束。

【讨论】:

  • 假设你使用的是bash,要获取tail的PID被杀死(发送信号),使用( echo $BASHPID > /tmp/PID_tail; tail -n + 1 -f your file ) | gzip ...
  • (参考上面的评论):如果你运行exec tail -n +1,那肯定会起作用;否则,您将依赖于可能存在或不存在的优化。 (你知道哪个版本的 bash 自动 execing 添加了子shell的最后一个组件吗?)
  • (参考上面的评论):不太清楚你的意思......(echo $BASHPID > ...; tail ... ) | gzip ... & 肯定会分叉两个进程,一个用于| 周围的每一侧。在子shell中,(... ; ... );肯定意味着这两个部分在同一个子shell中执行,因为它必须按顺序执行。
  • 是的,但是您不想要作为尾部父级的 shell 的 pid,而是尾部本身的 pid,不是吗?如果您运行tail 而不是exec tail,那么您可能会将父进程带到tail,除非您的bash 版本自动将最后一个进程设为exec
  • 是的。你说的对。我在这里重新发布以更正我的评论:假设您正在使用 bash,要获取要杀死的尾部的 PID(发送信号),请使用 ( echo $BASHPID > /tmp/PID_tail; exec tail -n + 1 -f yourfile ) | gzip ...
【解决方案2】:

来自tail manpage强调我的

使用--follow (-f),tail 默认跟随文件 描述符,这意味着即使尾部文​​件被重命名, tail 将继续跟踪它的结束。此默认行为是 当您真的想跟踪的实际名称时,这是不可取的 文件,而不是文件描述符(例如,日志轮换)。 使用 --follow=name 在这种情况下。这会导致 tail 跟踪命名的 文件以适应重命名、删除和创建的方式。

因此您提出的问题的解决方案是使用:

tail --follow=name LOGILE | gzip -c >> /faraway/log.gz

这样,当文件被删除时,tail 会停止读取它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-15
    • 2021-04-15
    相关资源
    最近更新 更多