Усечение файла во время его использования (Linux) - PullRequest
33 голосов
/ 11 июня 2009

У меня есть процесс, который записывает много данных в стандартный вывод, который я перенаправляю в файл журнала. Я хотел бы ограничить размер файла, периодически копируя текущий файл с новым именем и обрезая его.

Мои обычные методы усечения файла, например

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 ]

31 голосов
/ 16 июля 2010

Начиная с coreutils 7.0, есть команда truncate.

26 голосов
/ 12 июня 2009

Интересным в этих восстановленных файлах является то, что первые 128 КБ или около того будут равны нулю после того, как вы обрежете файл, скопировав /dev/null поверх него. Это происходит потому, что файл усекается до нулевой длины, но дескриптор файла в приложении по-прежнему указывает сразу после последней записи. Когда он записывает снова, файловая система рассматривает начало файла как все нулевые байты - без фактической записи нулей на диск.

В идеале вам следует попросить поставщика приложения открыть файл журнала с флагом O_APPEND. Это означает, что после усечения файла при следующей записи неявным образом будет выполняться поиск конца файла (что означает возврат к нулевому смещению), а затем записываться новая информация.


Этот код настраивает стандартный вывод, поэтому он находится в режиме O_APPEND, а затем вызывает команду, заданную его аргументами (скорее, как nice запускает команду после настройки ее уровня 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);
}

Мое тестирование было несколько случайным, но едва достаточным, чтобы убедить меня, что это сработало.


Более простая альтернатива

Билли отмечает в своем ответе , что '>>' является оператором добавления - и действительно, в 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:

Используйте перенаправление добавления, а не код обертки (' cantrip ') выше. Это просто говорит о том, что когда вы используете одну конкретную технику для других (допустимых) целей, адаптация ее к другой не обязательно является самым простым механизмом - даже если он работает.

12 голосов
/ 23 мая 2013

Перенаправить вывод, используя >> вместо>. Это позволит вам обрезать файл без возврата его исходного размера. Также не забудьте перенаправить STDERR (2> & 1).

Таким образом, конечный результат будет: myprogram >> myprogram.log 2>&1 &

10 голосов
/ 11 июня 2009

Взгляните на утилиту split(1), входящую в состав GNU Coreutils.

8 голосов
/ 11 июня 2009

Попробуйте > file.


Обновление, касающееся комментариев: у меня это хорошо работает:

robert@rm:~> echo "content" > test-file
robert@rm:~> cat test-file 
content
robert@rm:~> > test-file
robert@rm:~> cat test-file 
6 голосов
/ 25 июня 2014

У меня была похожая проблема на redhat v6, echo > file или > file приводила к сбоям в работе apache и tomcat, поскольку файлы журналов становились для них недоступными.

И исправление было странным

echo " " > file

очистит файл и не вызовет никаких проблем.

3 голосов
/ 20 июня 2011

Я скачал и скомпилировал последнюю версию coreutils, чтобы я мог иметь truncate в наличии.

Ран ./configure и make, но не работает make install.

Все скомпилированные утилиты появляются в папке "src".

Я побежал

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

в файле журнала 1,7 ГБ.

Он не изменил размер, указанный при использовании ls -l, но он освободил все дисковое пространство - что я действительно должен был сделать до того, как /var заполнил и убил процесс.

Спасибо за подсказку по "усечению"!

3 голосов
/ 12 июня 2009

В Linux (фактически, все политики) файлы создаются, когда они открываются и удаляются, когда ничто не содержит ссылки на них. В этом случае программа, которая открыла его, и каталог, в котором он был открыт «in», содержат ссылки на файл. Когда программа cp хочет выполнить запись в файл, она получает ссылку на него из каталога, записывает нулевую длину в метаданные, хранящиеся в каталоге (это небольшое упрощение), и отказывается от дескриптора. Затем исходная программа, по-прежнему сохраняющая оригинальный дескриптор файла, записывает в файл дополнительные данные и сохраняет то, что, по ее мнению, должна быть длина.

даже если вы удалите файл из каталога, программа продолжит записывать в него данные (и использовать место на диске), даже если ни одна другая программа не сможет ссылаться на него.

Короче говоря, когда у программы есть ссылка (дескриптор) на файл, вы ничего не измените.

теоретически есть способы изменить поведение программ, задав LD_LIBRARY_PATH для включения программы, которая перехватывает все системные вызовы доступа к файлам. Я помню, что видел где-то подобное, хотя не могу вспомнить имя.

3 голосов
/ 11 июня 2009

при использовании файла, если вы попытаетесь аннулировать его или что-то в этом роде, иногда это может «запутать» приложение, записывающее файл журнала, и после этого может ничего не записывать.

То, что я бы попробовал сделать, это установить прокси / фильтр для этого журнала вместо перенаправления в файл, перенаправления в процесс или что-то, что получит входные данные и запишет в скользящий файл.

Может быть, это можно сделать с помощью скрипта, иначе вы можете написать простое приложение для этого (Java или что-то еще). Влияние на производительность приложения должно быть довольно незначительным, но вам придется запустить несколько тестов.

Кстати, ваше приложение, это автономное веб-приложение, ...? Может быть, есть другие варианты для изучения.

Редактировать: есть также Оператор добавления перенаправления >> , который я лично никогда не использовал, но он может не заблокировать файл.

2 голосов
/ 05 ноября 2012

@ Hobo использует freopen () , он повторно использует поток, чтобы либо открыть файл, указанный в имени файла, либо изменить его режим доступа. Если указано новое имя файла, функция сначала пытается закрыть любой файл, уже связанный с потоком (третий параметр), и отключает его. Затем, независимо от того, был ли этот поток успешно закрыт или нет, freopen открывает файл, указанный в имени файла, и связывает его с потоком так же, как 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;
}
...