在使用文件时截断文件(Linux)

Hob*_*obo 34 unix linux logging truncate file

我有一个将大量数据写入stdout的进程,我将其重定向到日志文件.我想通过偶尔将当前文件复制到新名称并截断它来限制文件的大小.

我通常截断文件的技巧,比如

cp /dev/null file
Run Code Online (Sandbox Code Playgroud)

不起作用,大概是因为这个过程正在使用它.

有什么方法可以截断文件吗?或者删除它并以某种方式将进程'stdout与新文件关联?

FWIW,它是第三方产品,我无法修改以更改其日志记录模型.

EDIT重定向文件似乎与上面的副本有相同的问题 - 文件在下次写入时返回到之前的大小:

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
Run Code Online (Sandbox Code Playgroud)

Pet*_*aut 32

从coreutils 7.0开始,有一个truncate命令.

  • 总是(对于某些值总是)是C函数truncate().从coreutils 7.0开始,还有一个程序截断,它可能包含了C函数. (4认同)
  • 它真的适用于java应用程序吗?对我来说不是!即使“truncate -s 0 GC.log”几秒钟显示“截断”文件,在这段时间之后(我猜测 - 当应用程序写入更多时) - 文件会恢复到原始大小。 (2认同)

Jon*_*ler 27

这些重新生成文件的有趣之处在于,在通过复制文件截断文件后,前128 KB左右将全部为零/dev/null.发生这种情况是因为文件被截断为零长度,但应用程序中的文件描述符仍然在其最后一次写入后立即指向.当它再次写入时,文件系统将文件的开头视为所有零字节 - 而不实际将零写入磁盘.

理想情况下,您应该要求应用程序的供应商使用该O_APPEND标志打开日志文件.这意味着在截断文件后,下一次写入将隐式地寻找文件的末尾(意味着返回到零偏移),然后写入新信息.


这个代码装备了标准输出,因此它处于O_APPEND模式状态,然后调用其参数给出的命令(比如nice在调整其良好级别nohup后运行命令,或者在修复之后运行命令,因此它忽略了SIGHUP).

#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);
}
Run Code Online (Sandbox Code Playgroud)

我对它的测试有点随意,但几乎不足以说服我它有效.


更简单的选择

Billy在他的回答中指出' >>'是附加运算符 - 事实上,在Solaris 10上,bash(版本3.00.16(1))确实使用了O_APPEND标志 - 从而使上面的代码不必要,如图所示('Black JL:'是我在这台机器上的提示):

Black JL: truss -o bash.truss bash -c "echo Hi >> x3.29"
Black JL: grep open bash.truss
open("/var/ld/ld.config", O_RDONLY)             Err#2 ENOENT
open("/usr/lib/libcurses.so.1", O_RDONLY)       = 3
open("/usr/lib/libsocket.so.1", O_RDONLY)       = 3
open("/usr/lib/libnsl.so.1", O_RDONLY)          = 3
open("/usr/lib/libdl.so.1", O_RDONLY)           = 3
open("/usr/lib/libc.so.1", O_RDONLY)            = 3
open("/platform/SUNW,Ultra-4/lib/libc_psr.so.1", O_RDONLY) = 3
open64("/dev/tty", O_RDWR|O_NONBLOCK)           = 3
stat64("/usr/openssl/v0.9.8e/bin/bash", 0xFFBFF2A8) Err#2 ENOENT
open64("x3.29", O_WRONLY|O_APPEND|O_CREAT, 0666) = 3
Black JL:
Run Code Online (Sandbox Code Playgroud)

使用追加重定向而不是上面的包装器(' cantrip ')代码.这只是表明当你将一种特定技术用于其他(有效)目的时,将其适用于另一种技术并不一定是最简单的机制 - 即使它有效.

  • @Hobo:如@Billy 的回答中所述,只需使用 &gt;&gt; 而不是 &gt; 重定向标准输出。 (2认同)

Jon*_*ono 12

使用>>而不是>重定向输出.这将允许您截断文件,而文件不会恢复到原始大小.另外,不要忘记重定向STDERR(2>&1).

所以最终的结果是: myprogram >> myprogram.log 2>&1 &


Mic*_*ngh 9

看看该实用程序split(1),GNU Coreutils的一部分.

  • 这是选择的解决方案似乎很奇怪.它没有减少文件的大小; 它所做的就是给你文件各部分和原件的副本. (11认同)
  • 投反对票。需要更多详细的答案才能达到如此质量。 (8认同)
  • 是的,这就是我需要的,而不是我所要求的.我将通过拆分重定向,而不是直接重定向到日志文件,这将限制各个日志文件的大小. (5认同)
  • 对于那些摸不着头脑的人来说,这是什么意思:$ your_command | split --bytes 100M - 前缀 (3认同)

Rob*_*anu 8

试试> file.


有关评论的更新:它对我很有用:

robert@rm:~> echo "content" > test-file
robert@rm:~> cat test-file 
content
robert@rm:~> > test-file
robert@rm:~> cat test-file 
Run Code Online (Sandbox Code Playgroud)

  • 我也看到过这种行为.几乎看起来文件指针被更改为新的空文件.但是正在运行的进程可以处理旧文件.写入时,它会将文件指针更改回旧文件. (2认同)

Art*_*ldt 6

在 Linux(实际上是所有 unicies)中,文件在打开时被创建,当没有任何文件引用它们时会被删除。在这种情况下,打开它的程序和打开它的目录包含对文件的引用。当 cp 程序想要写入文件时,它会从目录中获取对该文件的引用,将长度为零的内容写入存储在目录中的元数据(这是一个轻微的简化)并放弃句柄。然后,仍然持有原始文件句柄的原始程序将更多数据写入文件并保存它认为应该是的长度。

即使您从目录中删除文件,程序也会继续向其中写入数据(并占用磁盘空间),即使没有其他程序可以引用它。

简而言之,一旦程序具有对文件的引用(句柄),您所做的任何事情都不会改变它。

理论上,通过设置 LD_LIBRARY_PATH 来包含一个拦截所有文件访问系统调用的程序来修改程序行为。我记得在某处看到过类似的东西,虽然记不起名字。


小智 6

我在redhat v6上遇到了类似的问题,echo > file或者> file导致apache和tomcat出错,因为日志文件对他们来说无法访问.

修复很奇怪

echo " " > file
Run Code Online (Sandbox Code Playgroud)

会清理文件,不会造成任何问题.