Подтвердить что ты не робот

Усечение файла во время его использования (Linux)

У меня есть процесс, который записывает много данных в 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
4b9b3361

Ответ 1

Взгляните на утилиту split(1), часть GNU Coreutils.

Ответ 2

Интересная вещь об этих регрессированных файлах заключается в том, что первые 128 КБ или около того будут все нули после того, как вы обрезаете файл, скопировав /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);
}

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


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

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:

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

Ответ 3

Как и в coreutils 7.0, существует команда truncate.

Ответ 4

Перенаправить вывод с помощью → вместо > . Это позволит вам обрезать файл без восстановления файла до его первоначального размера. Кроме того, не забудьте переадресовать STDERR (2 > & 1).

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

Ответ 6

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

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

echo " " > file

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

Ответ 7

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

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

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

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

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

Ответ 8

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

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

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

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

Ответ 9

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

kill -HUP [process-id]

И затем он снова начнет писать.

В качестве альтернативы (как предложил Билли) возможно перенаправление вывода из приложения на программу ведения журнала, такую ​​как многоязычная или та, которая обычно используется с Apache, известная как cronolog. Тогда у вас будет более тонкий контроль над тем, где все идет до того, как оно будет записано в этот начальный файловый дескриптор (файл), который на самом деле все это есть.

Ответ 10

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

Ran ./configure и make, но не выполнил make install.

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

Я побежал

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

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

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

Спасибо за подсказку "truncate"!

Ответ 11

@Hobo использует freopen(), он повторно использует поток, чтобы либо открыть файл, указанный по имени файла, либо изменить его режим доступа. Если указано новое имя файла, функция сначала пытается закрыть любой файл, уже связанный с потоком (третий параметр), и отключает его. Затем, независимо от того, был ли этот поток успешно закрыт или нет, freopen открывает файл, указанный по имени файла, и связывает его с потоком, как это сделал бы fopen, используя указанный режим.

если двоичный файл thirdparty генерирует журналы, нам нужно написать оболочку, которая будет вращать журналы, а thirdparty будет работать в потоке 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;
}

Ответ 12

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

Ответ 13

У меня была аналогичная проблема и мне не удалось выполнить "tail -f" на выходе script, который был запущен cron:

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

Я исправил это, изменив перенаправление stderr:

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