代码之家  ›  专栏  ›  技术社区  ›  Hobo

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

  •  32
  • Hobo  · 技术社区  · 16 年前

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

    我常用的截断文件的技术,比如

    cp /dev/null file
    

    不工作,可能是因为流程正在使用它。

    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
    
    13 回复  |  直到 16 年前
        1
  •  32
  •   Peter Eisentraut    14 年前

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

        2
  •  32
  •   Community CDub    8 年前

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

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


    此代码装备标准输出,因此它处于 模式,然后调用由其参数给出的命令(类似于 nice 在调整好命令的级别后运行命令,或 nohup

    #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);
    }
    

    我对它的测试有点随意,但仅仅足以让我相信它是有效的。


    更简单的选择

    Billy 他的笔记 answer >> '是append运算符—实际上,在Solaris 10上,bash(版本3.00.16(1))使用 O_追加

    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:
    

    使用附加重定向而不是包装器(' cantrip )上面的代码。这只是为了说明,当您将一种特定技术用于其他(有效)目的时,将其适应于另一种并不一定是最简单的机制——即使它可以工作。

        3
  •  13
  •   Jono    12 年前

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

    因此,最终结果将是: myprogram >> myprogram.log 2>&1 &

        4
  •  9
  •   Robert Munteanu    16 年前

    尝试 > file


    robert@rm:~> echo "content" > test-file
    robert@rm:~> cat test-file 
    content
    robert@rm:~> > test-file
    robert@rm:~> cat test-file 
    
        5
  •  7
  •   aish    10 年前

    我在redhat v6上遇到了类似的问题, echo > file > file

    而且修理很奇怪

    echo " " > file
    

    将清理文件,不会导致任何问题。

        6
  •  6
  •   Arthur Ulfeldt    16 年前

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

    即使要从目录中删除文件,程序也会继续向其中写入数据(并耗尽磁盘空间),即使其他程序无法引用它。

        7
  •  5
  •   Michiel Buddingh    16 年前

    看看这个实用程序 split(1) ,GNU Coreutils的一部分。

        8
  •  3
  •   Billy    16 年前

    我会尝试为该日志设置一种代理/过滤器,而不是重定向到文件、重定向到进程或获取输入并写入滚动文件的东西。

    也许它可以通过脚本来完成,否则你可以为此编写一个简单的应用程序(java或其他东西)。对应用程序性能的影响应该很小,但您必须运行一些测试。

    Append Redirection Operator >> 我个人从未使用过,但它可能不会锁定文件。

        9
  •  3
  •   Tony Rad    12 年前

    我下载并编译了最新的 coreutils 所以我可以 truncate 可获得的

    ./configure make ,但没有跑 make install .

    所有编译的实用程序都显示在“src”文件夹中。

    我跑

    [path]/src/truncate -s 1024000 textfileineedtotruncate.log

    在1.7 GB日志文件上。

    使用时未更改列出的大小 ls -l /var 填充并终止进程。

    谢谢你关于“截断”的提示!

        10
  •  2
  •   Vex    16 年前

    您是否检查了任何信号的行为,如SIGHUP到第三方产品,以查看它是否将开始记录新文件?首先,将旧文件移动到永久名称。

    kill-HUP[进程id]

    然后它会再次开始写出来。

    或者(正如Billy所建议的)可能会将应用程序的输出重定向到一个日志程序(如multilog)或Apache常用的程序(称为cronolog)。然后,在将所有内容写入初始文件描述符(file)之前,您将对其进行更细粒度的控制,这实际上就是它的全部内容。

        11
  •  2
  •   Arthur Ulfeldt    16 年前

    您可以通过管道将其传送到一个程序,该程序通过关闭、移动并在文件过大时打开一个新文件来自动旋转该文件,而不是将其重定向到一个文件。

        12
  •  2
  •   Yogesh    12 年前

    @流浪汉使用 ,它重用流来打开由文件名指定的文件或更改其访问模式。 如果指定了新的文件名,函数将首先尝试关闭已与流关联的任何文件(第三个参数)并解除其关联。然后,与流是否成功关闭无关,freopen打开由filename指定的文件并将其与流关联,就像fopen使用指定模式所做的那样。

    #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;
    }
    
        13
  •  1
  •   lfjeff    8 年前

        * * * * * my_script >> /var/log/my_script.log 2>&1
    

    我通过更改stderr重定向来修复它:

        * * * * * my_script >> /var/log/my_script.log 2>/var/log/my_script.err