我有一个将大量数据写入stdout的进程,我将其重定向到日志文件。我想通过偶尔将当前文件复制到新名称并截断它来限制文件的大小。
我常用的截断文件的技巧,比如
cp /dev/null file
不起作用,大概是因为该过程正在使用它。
有什么方法可以截断文件吗?或者删除它并以某种方式将进程'stdout与新文件关联起来?
FWIW,它是第三方产品,我无法修改以更改其日志记录模型。
编辑重定向文件似乎与上面的副本有相同的问题 - 文件在下次写入时返回到之前的大小:
ls -l sample.log ; echo > sample.log ; ls -l sample.log ; sleep 10 ; ls -l sample.log
-rw-rw-r-- 1 user group 1291999 Jun 11 2009 sample.log
-rw-rw-r-- 1 user group 1 Jun 11 2009 sample.log
-rw-rw-r-- 1 user group 1292311 Jun 11 2009 sample.log
答案 0 :(得分:31)
从coreutils 7.0开始,有一个truncate
命令。
答案 1 :(得分:26)
这些重新生成文件的有趣之处在于,通过在文件上复制/dev/null
来截断文件后,前128 KB左右将全部为零。发生这种情况是因为文件被截断为零长度,但应用程序中的文件描述符仍然在其最后一次写入后立即指向。当它再次写入时,文件系统将文件的开头视为所有零字节 - 而不实际将零写入磁盘。
理想情况下,您应该要求应用程序的供应商使用O_APPEND
标志打开日志文件。这意味着在截断文件后,下一次写入将隐式搜索到文件的末尾(意味着返回到零偏移),然后写入新信息。
此代码装配标准输出,因此它处于O_APPEND
模式,然后调用其参数给出的命令(而非nice
调整其良好级别后运行命令,或{{1}在修复之后运行命令,因此忽略了SIGHUP)。
nohup
我对它的测试有点随意,但几乎不足以说服我它有效。
Billy在他的answer中注意到'#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
static char *arg0 = "<unknown>";
static void error(const char *fmt, ...)
{
va_list args;
int errnum = errno;
fprintf(stderr, "%s: ", arg0);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
putc('\n', stderr);
fflush(0);
exit(1);
}
int main(int argc, char **argv)
{
int attr;
arg0 = argv[0];
if (argc < 2)
error("Usage: %s cmd [arg ...]", arg0);
if ((attr = fcntl(1, F_GETFL, &attr)) < 0)
error("fcntl(F_GETFL) failed");
attr |= O_APPEND;
if (fcntl(1, F_SETFL, attr) != 0)
error("fcntl(F_SETFL) failed");
execvp(argv[1], &argv[1]);
error("failed to exec %s", argv[1]);
return(1);
}
'是附加运算符 - 实际上,在Solaris 10上,bash(版本3.00.16(1))确实使用了{ {1}}标志 - 从而使上面的代码变得不必要,如图所示('Black JL:'是我在这台机器上的提示):
>>
使用追加重定向而不是上面的包装器('cantrip')代码。这只是表明当你将一种特定技术用于其他(有效)目的时,将其适用于另一种技术并不一定是最简单的机制 - 即使它有效。
答案 2 :(得分:12)
使用&gt;&gt;重定向输出而不是&gt;。这将允许您截断文件,而文件不会恢复到原始大小。另外,不要忘记重定向STDERR(2&gt;&amp; 1)。
所以最终结果是:myprogram >> myprogram.log 2>&1 &
答案 3 :(得分:10)
查看实用程序split(1)
,它是GNU Coreutils的一部分。
答案 4 :(得分:8)
尝试> file
。
关于评论的更新:它对我很有用:
robert@rm:~> echo "content" > test-file
robert@rm:~> cat test-file
content
robert@rm:~> > test-file
robert@rm:~> cat test-file
答案 5 :(得分:6)
我在redhat v6上有类似的问题,echo > file
或> file
导致apache和tomcat出错,因为日志文件对他们来说无法访问。
修复很奇怪
echo " " > file
会清理文件,不会造成任何问题。
答案 6 :(得分:3)
正在使用该文件,如果您尝试使其无效或类似,有时可能会“混淆”正在写入日志文件的应用程序,并且之后可能不会记录任何内容。
我尝试做的是为该日志设置一种代理/过滤器,而不是重定向到文件,重定向到进程或可以获取输入并写入滚动文件的内容。
也许它可以通过脚本完成,否则你可以为它编写一个简单的应用程序(java或其他东西)。对应用程序性能的影响应该非常小,但您必须运行一些测试。
顺便说一下,你的应用程序,它是一个独立的网络应用程序,......?也许还有其他选择需要调查。
编辑:还有一个我个人从未使用的Append Redirection Operator >>,但它可能无法锁定该文件。
答案 7 :(得分:3)
在Linux(实际上是所有unicies)文件在打开时被创建,并且在没有任何内容引用时被删除。在这种情况下,打开它的程序和它打开的目录'in'保存对文件的引用。当cp程序想要写入文件时,它从目录中获取对它的引用,将长度为零写入存储在目录中的元数据(这是一个轻微的简化)并放弃句柄。然后原始程序仍然保留原始文件句柄,将更多数据写入文件并保存它认为长度应该是什么。
即使您从目录中删除文件的位置,程序仍会继续向其写入数据(并占用磁盘空间),即使其他程序没有任何方法可以引用它。
简而言之,一旦程序对文件有一个引用(句柄),你所做的就是改变它。
理论上,通过将LD_LIBRARY_PATH设置为包含拦截所有文件访问系统调用的程序,可以修改程序行为。我记得在某个地方看到过这样的东西,虽然不记得这个名字。
答案 8 :(得分:3)
我下载并编译了最新的coreutils
,因此我可以使用truncate
。
启用./configure
和make
,但未运行make install
。
所有已编译的实用程序都显示在“src”文件夹中。
我跑了
[path]/src/truncate -s 1024000 textfileineedtotruncate.log
在1.7 GB的日志文件中。
它没有改变使用ls -l
时列出的大小,但它确实释放了所有磁盘空间 - 这是我在/var
填满并杀死进程之前真正需要做的事情。 / p>
感谢您提示“截断”!
答案 9 :(得分:2)
您是否检查过SIGHUP等信号对第三方产品的行为,看它是否会开始记录新文件?您可以先将旧文件移动到永久名称。
kill -HUP [process-id]
然后它会再次开始写出来。
或者(正如Billy建议的那样)可能会将应用程序的输出重定向到日志程序(如multilog)或Apache常用的程序(称为cronolog)。然后,在将其写入初始文件描述符(文件)之前,您将对所有内容进行更精细的控制,这实际上就是它。
答案 10 :(得分:2)
@Hobo使用 freopen(),它重用流来打开filename指定的文件或更改其访问模式。 如果指定了新文件名,则该函数首先尝试关闭已与stream(第三个参数)关联的任何文件并取消关联。然后,无论该流是否成功关闭,freopen都会打开由filename指定的文件,并将其与流关联,就像fopen使用指定的模式一样。
如果第三方二进制文件生成日志,我们需要编写一个旋转日志的包装器,第三方将在proxyrun线程中运行,如下所示。
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <unistd.h>
#include <string.h>
using namespace std;
extern "C" void * proxyrun(void * pArg){
static int lsiLineNum = 0;
while(1)
{
printf("\nLOGGER: %d",++lsiLineNum);
fflush(stdout);
}
return NULL;
}
int main(int argc, char **argv)
{
pthread_t lThdId;
if(0 != pthread_create(&lThdId, NULL, proxyrun, NULL))
{
return 1;
}
char lpcFileName[256] = {0,};
static int x = 0;
while(1)
{
printf("\n<<<MAIN SLEEP>>>");
fflush(stdout);
sprintf(lpcFileName, "/home/yogesh/C++TestPrograms/std.txt%d",++x);
freopen(lpcFileName,"w",stdout);
sleep(10);
}
return 0;
}
答案 11 :(得分:1)
而不是将其重定向到文件,您可以将其传输到一个程序,该程序通过关闭它,移动它并在每次变得太大时打开一个新文件来自动旋转文件。
答案 12 :(得分:1)
我遇到了类似的问题而无法做到&#34; tail -f&#34;在从cron运行的脚本的输出上:
* * * * * my_script >> /var/log/my_script.log 2>&1
我通过更改stderr重定向来修复它:
* * * * * my_script >> /var/log/my_script.log 2>/var/log/my_script.err