Linux Bash shell脚本中的IO重定向无法重新创建被移动/删除的文件?

5

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

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

这个工作得很好,我得到了所需的行为。

现在有一个后台进程监视run.log和err.log文件,如果日志文件大小超过一定阈值,就将它们移动到其他文件名中。

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

我的期望是,在这个文件移动发生之后,myprog输出重定向将创建err.log文件,并将新的输出写入到这个新文件中。然而,在我的日志文件监视进程移动文件之后,err.log或run.log再也没有被创建,尽管myprog继续正常运行。

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


文件句柄与索引节点(inode)相关联,而非文件名。如果对文件系统上的哪个名称(如果有)索引节点链接进行更改,则完全透明。这是非常理想的行为——例如,在 UNIX 类操作系统上使用软件时,您可以升级软件包。 - Charles Duffy
3个回答

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

当日志文件大小达到50兆字节时,将进行日志轮换。

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


1
这并没有错,但我认为它是不完整的——没有提供任何关于如何在不重启整个脚本的情况下重新启动 shell 脚本日志记录的提示。像 logrotate 一样,rotatelogs 会为您执行旋转操作,但您仍然需要发出使用新目标文件的信号。 - Charles Duffy
它不需要信号,这正是我说“如果重新打开它不可能(即您无法更改可执行文件或重新启动它)”的原因。实际使用方法是“myprog | rotatelogs <parameters>”。这样,rotatelogs将进行旋转并重新打开文件。 - Vitor
啊,以那种使用方式来看,这很有道理。 - Charles Duffy

0

支持日志轮换的软件实际上已经有了对此轮换的支持编写。如果您查看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
   }

即发送HUP信号给已经轮换日志的程序;该程序具有一个 信号处理器,重新打开其输出文件。


你也可以在你的shell脚本中这样做:

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

...然后,在旋转日志之后,运行kill -HUP pid_of_yourscript; 在脚本本身执行命令的下一个场合(因为信号处理程序只在前台可执行文件之间运行),它将重新打开其输出以重新创建日志文件,而无需重新启动。


0
如果我必须猜测,它实际上将记录进程与文件描述符相关联,而不是文件名。当你重命名它时,只改变了文件名。因此,该进程继续记录到该文件中。只是一个猜测。如果让我修复它,我会停止记录过程,并在那一点上重新启动它以重新关联正确的文件。

只是一个猜测。


你说得完全正确。我刚刚注意到行为正是你在这里解释的那样。在mv命令之后,我的文件仍然在不断增长。既然我知道了这种行为,我将能够想出一个解决方案,可能涉及文件复制(cp)而不是移动,并截断原始文件。 - ancient demon
1
@user1824801,使用cp会让你面临竞态条件的问题。移动原始文件,然后使用信号处理程序触发重新打开日志目标是Apache和其他专业软件用于此目的的实际做法。 - Charles Duffy
是的,谢谢@Charles Duffy。我意识到cp和truncate会导致竞争条件,如果我的程序赢了我的日志监视器,我将在此过程中丢失一些日志语句。 - ancient demon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接