【问题标题】:bash script to make `tail -f` exit when the file is moved or deleted当文件被移动或删除时,bash脚本使“tail -f”退出
【发布时间】:2014-03-15 10:06:06
【问题描述】:

当前删除、移动或重命名运行有tail -f 的文件没有任何作用,我希望它中止。我已经阅读了手册页,似乎 -f 应该在文件移动时中止并且 -F 将跟随文件但在 Mac OS X 上似乎 -f 和 -F 是相同的。如何编写一个 bash 脚本,在文件移动后让 tail -f 干净地退出?

【问题讨论】:

  • 这对于 SO 来说是题外话,但检查手册页会显示你有它倒退developer.apple.com/library/mac/documentation/Darwin/Reference/… 但此外,-F 然后将尾随在同一位置创建的新文件与同名。
  • 手册页说,如果文件被重命名,-F 将另外跟随文件。滑稽的 -f 也会这样做。
  • @schellsan:确实在 OSX-f-F 在文件重命名后都跟随文件,但 -F 做了一些 -f 没有的事情t:如果 original 名称下的文件再次出现,it 将从那时起再次 被尾随。在 Linux 上,-F 的行为更直观:如果原始文件被重命名,拖尾将暂停并显示错误消息 - 直到重新出现以原始名称命名的 [新] 文件。也就是说,要实现您想要的,您确实需要一个程序化解决方案,如您自己的答案中所提供的那样;另见我的。
  • 我真的不明白为什么 shell 编程的细节与 SO 无关。 Shell 编程就是编程,而获得编程问题的答案正是 SO 的目的。
  • @jonathan-leffler ,我同意 - 我正在编写一个 bash 脚本,并且需要将其作为其中的一部分,因此它似乎非常符合主题。

标签: linux macos bash unix


【解决方案1】:
  • Linux 上,您可以使用tail --follow=name(而不仅仅是-f,相当于--follow=descriptor)来实现您想要的,但前提是文件被删除而不是已移动 - 一旦文件被删除,将报告错误消息并退出tail(代码为 1);遗憾的是,相比之下,如果文件只是被 MOVED(重命名),tail 不会退出 - 需要编程解决方案。
  • OSX 上,您始终需要一个编程解决方案 - 无论文件是移动还是删除。

bash 脚本,用于在目标文件不再存在(以其原始名称)时退出拖尾 - 来自@schellsan 自己的答案的更强大的脚本公式:

#!/usr/bin/env bash

tail -f "$1" &  # start tailing in the background
while [[ -f $1 ]]; do sleep 0.1; done # periodically check if target still exists
kill $! 2>/dev/null || : # kill tailing process, ignoring errors if already dead
  • 正确处理需要引用的文件名(例如,带有嵌入空格的名称)。
  • 通过睡眠 在文件存在检查之间防止创建紧密循环 - 根据需要调整睡眠持续时间;警告:某些平台仅支持 integral 秒。

如果需要更高的稳健性,这里有一个版本:

  • 通过退出陷阱杀死后台进程,以确保无论脚本本身如何退出(通常是通过 Control-C 退出),它都会被杀死。
  • 如果发现后台进程不再活动,则退出脚本。
#!/usr/bin/env bash

# Set an exit trap to ensure that the tailing process
# - to be created below - is terminated, 
# no matter how this script exits.
trap '[[ -n $tailPid ]] && kill $tailPid 2>/dev/null' EXIT

# Start the tailing process in the background and
# record its PID.
tail -f "$1" & tailPid=$!

# Stay alive as long as the target file exists.
while [[ -f $1 ]]; do
  # Sleep a little.
  sleep 0.1
  # Exit if the tailing process died unexpectedly.
  kill -0 $tailPid 2>/dev/null || { tailPid=; exit; }
done

【讨论】:

  • 酷。顺便说一句,最后一行的|| : 有什么作用?
  • @ToddLehman:它将$tailPid 变量设置为空字符串然后退出,这会触发trap 处理程序,现在$tailPid 不再具有非空值(@987654332 @) - 在退出之前跳过不再需要的 kill 调用。
  • 抱歉,我指的是 first 代码块的最后一行——特别是 || : 部分。但是感谢您对第二个代码块的最后一行的解释。 :)
  • @ToddLehman:哎呀! || : 忽略kill 报告的任何故障,当它发生时执行:。内置的: 是一个总是成功的无操作,因此整个语句报告的退出代码保证为0。重置退出代码并不是绝对必要的,但如果该语句恰好是脚本中的最后一个,那么您实际上并不关心的 kill 失败将作为脚本的退出代码中继。
【解决方案2】:

以防万一其他人遇到此问题,您可以使用一个小脚本,在其中将 tail 作为后台进程运行,然后循环直到文件被移动,从而终止 tail 进程。

#!/bin/bash

tail -f $1 &
pid=$!

while [ -f $1 ]
do
    if [ ! -f $1 ]
    then
        kill -9 $pid
    fi
done

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 1970-01-01
    • 2021-01-17
    • 2016-09-26
    • 1970-01-01
    • 2014-10-28
    相关资源
    最近更新 更多