Linux Bash shell 脚本中的 IO 重定向未重新创建 moved/deleted 文件?

IO Redirection in Linux Bash shell scripts not recreating moved/deleted file?

我对 Linux 上的 shell 编程很陌生,在我的 Linux 实例中,我按照以下方式将程序的标准输出和标准错误重定向到两个文件 运行 它在后台

myprog > run.log 2>> err.log &

这很好用,我得到了我想要的行为

现在有另一个后台进程监视 run.log 和 err.log,如果日志文件增长超过某个阈值,则将它们移动到其他文件名。

例如mv err.log err[date-time].log

我的预期是,在此文件移动发生后,err.log 将由 myprog 输出重定向再次创建,并将新输出写入该新文件。但是,在我的日志文件监视进程移动文件后,err.log 或 run.log 再也不会创建,尽管 myprog 继续 运行 没有任何问题。

这是 Linux 中的正常行为吗?如果是,我应该怎么做才能让我的预期行为发挥作用?

是的,是的。除非您先重新打开文件,否则它将继续写入旧文件,即使您无法再访问它。事实上,被删除的文件使用的 space 只有在每个进程关闭它后才可用。如果重新打开它是不可能的(即你不能更改可执行文件也不能重新启动它),那么像 http://httpd.apache.org/docs/2.4/programs/rotatelogs.html 这样的解决方案是你最好的选择。 它可以根据文件大小或时间轮换日志,甚至轮换后调用自定义脚本。

用法示例:

myprog | rotatelogs logname.log 50M

这样,每当大小达到 50 兆字节时,日志就会轮换。

[编辑:指向更新版本的 rotatelogs]

如果我不得不猜测的话,它实际上将记录日志的进程与文件描述符相关联,而不是文件名。重命名时,您只更改文件名。所以这个过程只是不断地记录到文件中。只是一个猜测。如果我的任务是修复它,我会停止记录过程并在那时重新启动它以将它与正确的文件重新关联。

只是猜测。

支持日志轮换的软件实际上已经支持写入这种轮换。如果您查看 man logrotate,您会注意到典型的配置如下所示:

   "/var/log/httpd/access.log" /var/log/httpd/error.log {
       rotate 5
       mail www@my.org
       size 100k
       sharedscripts
       postrotate
           /usr/bin/killall -HUP httpd
       endscript
   }

...也就是说它向log被轮转的程序发送了一个HUP信号;该程序有一个 信号处理程序 可以重新打开其输出文件。


您也可以在 shell 脚本中执行此操作:

reopen_logs() {
  exec >>run.log 2>>err.log
}
trap reopen_logs HUP

...然后,在轮换日志后,运行 kill -HUP pid_of_yourscript;下次当脚本本身正在执行命令时(因为信号处理程序仅 运行 在前台可执行文件之间),它将重新打开其输出以重新创建日志文件而无需重新启动。