Tronquer un fichier pendant qu'il est utilisé (Linux)

j'ai un processus qui écrit beaucoup de données à stdout, que je redirige vers un fichier journal. J'aimerais limiter la taille du fichier en copiant occasionnellement le fichier courant à un nouveau nom et en le tronquant.

mes techniques habituelles de tronçonnage d'un fichier, comme

cp /dev/null file

ne fonctionne pas, probablement parce que le processus l'utilise.

est-ce que je peux tronquer le fichier? Ou l'effacer et d'une façon ou d'une autre associer le stdout du processus avec un nouveau fichier?

FWIW, c'est un produit tiers que je ne peux pas Modifier pour changer son modèle d'enregistrement.

EDIT rediriger sur le fichier semble avoir le même problème que la copie ci - dessus-le fichier retourne à sa taille précédente la prochaine fois qu'il est écrit à:

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
29
demandé sur Hobo 2009-06-11 13:55:58

13 réponses

regardez l'utilitaire split(1) , qui fait partie de GNU Coreutils.

11
répondu Michiel Buddingh 2009-06-11 10:00:52

à partir de coreutils 7.0, il y a une commande truncate .

27
répondu Peter Eisentraut 2010-07-16 11:03:40

la chose intéressante à propos de ces fichiers se régénèrent est que les 128 premiers Ko seront tous des zéros après que vous avez tronqué le fichier en copiant /dev/null sur lui. Cela se produit parce que le fichier est tronqué à la longueur zéro, mais le descripteur de fichier dans l'application pointe encore immédiatement après sa dernière écriture. Quand il écrit encore, le système de fichiers traite le début du fichier, comme tous les octets zéro - sans écrire des zéros sur le disque.

Idéalement, vous doit demander au vendeur de l'application d'ouvrir le fichier journal avec le drapeau O_APPEND . Cela signifie qu'après avoir tronqué le fichier, la prochaine écriture cherchera implicitement jusqu'à la fin du fichier (ce qui signifie Revenir à offset zéro), puis écrira les nouvelles informations.


ce code modifie la sortie standard de sorte qu'il est en mode O_APPEND et puis invoque la commande donnée par ses arguments (un peu comme nice exécute une commande après ajustement de son nice-level, ou nohup exécute une commande après avoir réparé les choses afin qu'il ignore 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);
}

mes tests étaient quelque peu occasionnels, mais à peine suffisants pour me convaincre que cela a fonctionné.


alternative plus simple

Billy notes dans sa réponse que " >> " est l'opérateur annexe - et en effet, sur Solaris 10, bash (version 3.00.16(1)) utilise le drapeau O_APPEND - rendant ainsi le code ci-dessus inutile, comme indiqué ('Black JL:' est mon invite sur cette machine):

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:

utilisez la redirection d'ajout plutôt que le code wrapper (' cantrip ') ci-dessus. Cela montre simplement que lorsque vous utilisez une technique particulière à d'autres fins (valables), l'adapter à une autre n'est pas nécessairement le mécanisme le plus simple - même si cela fonctionne.

23
répondu Jonathan Leffler 2017-05-23 10:31:19

rediriger la sortie en utilisant > > au lieu de >. Cela vous permettra de tronquer le fichier sans le fichier de revenir à sa taille d'origine. Aussi, n'oubliez pas de rediriger STDERR (2>&1).

ainsi le résultat final serait: myprogram >> myprogram.log 2>&1 &

12
répondu Jono 2013-05-23 17:46:49

Essayer > file .


mise à Jour concernant les commentaires: il fonctionne très bien pour moi:

robert@rm:~> echo "content" > test-file
robert@rm:~> cat test-file 
content
robert@rm:~> > test-file
robert@rm:~> cat test-file 
8
répondu Robert Munteanu 2009-06-11 10:54:32

j'ai eu un problème similaire sur redhat v6, echo > file ou > file causait apache et tomcat à aller faulty que les fichiers journaux deviendraient inaccessible pour eux.

Et le correctif a été étrange

echo " " > file

nettoierait le fichier et ne causerait aucun problème.

5
répondu aishu 2014-06-25 12:07:51

comme le fichier est utilisé, si vous essayez de l'annuler ou quelque chose comme ça, parfois il pourrait "confondre" l'application qui est écrit dans le fichier journal et il pourrait ne pas enregistrer quoi que ce soit après cela.

ce que j'essaierais de faire est de configurer une sorte de proxy/filtre pour ce journal, au lieu de rediriger vers le fichier, rediriger vers un processus ou quelque chose qui obtiendrait l'entrée et l'écriture dans un fichier roulant.

peut-être que cela peut être fait par script Sinon vous pourriez écrire une simple application ( java ou autre ). L'impact sur la performance de l'application devrait être assez faible, mais vous devrez effectuer quelques tests.

Btw, votre application, est-il un autonome, application web,... ? Il y a peut-être d'autres options à étudier.

Edit: il y a aussi un opérateur de Redirection D'ajout >> que je n'ai personnellement jamais utilisé, mais il pourrait ne pas verrouiller le fichier.

3
répondu Billy 2009-06-11 10:54:56

sous Linux (en fait toutes les unicies) les fichiers sont créés lorsqu'ils sont ouverts et supprimés lorsque rien ne contient de référence à ces fichiers. Dans ce cas, le programme qui l'a ouvert et le répertoire dans lequel il a été ouvert contiennent des références au fichier. Quand le programme cp veut écrire dans le fichier il obtient une référence à lui du répertoire, écrit une longueur de zéro dans les métadonnées stockées dans le répertoire (c'est une légère simplification) et donne la poignée. Ensuite, le programme d'origine, encore en maintenant la poignée du fichier original, écrit plus de données dans le fichier et sauve ce qu'il pense que la longueur devrait être.

même si vous supprimez le fichier à partir du répertoire, le programme continuera à écrire des données (et l'utilisation de l'espace disque), même si aucun autre programme aurait aucun moyen de référence.

bref une fois que le programme a une référence (poignée) pour un fichier, rien ne va changer ça.

y sont en théorie des façons de modifier le comportement des programmes en définissant LD_LIBRARY_PATH pour inclure un programme qui intercepte tous les appels du système d'accès aux fichiers. Je me souviens avoir vu quelque chose comme ça quelque part, mais je ne me souviens pas du nom.

3
répondu Arthur Ulfeldt 2009-06-12 02:15:41

j'ai téléchargé et compilé le dernier coreutils pour avoir truncate disponible.

Couru ./configure et make , mais n'a pas 151940920" .

tous les utilitaires compilés apparaissent dans le dossier" src".

j'ai couru

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

sur un fichier log de 1,7 Go.

il n'a pas changé la taille indiquée en utilisant ls -l , mais il a fait libérer tout l'espace disque - ce qui est ce que j'avais vraiment besoin de faire avant /var rempli et tué le processus.

Merci pour le tuyau sur "truncate"!

3
répondu Larry Irwin 2012-11-19 04:56:09

avez-vous vérifié le comportement de signaux comme SIGHUP vers le produit tiers, pour voir s'il va commencer la journalisation d'un nouveau fichier? Vous devez déplacer l'ancien fichier d'un nom, d'abord.

kill-HUP [id du processus]

et puis il commencerait à écrire à nouveau.

alternativement (comme Billy l'a suggéré) peut-être rediriger la sortie de l'application vers un programme de journalisation comme multilog ou celui qui est couramment utilisé avec Apache, connu sous le nom de cronolog. Ensuite, vous aurez un contrôle plus fin de l'endroit où tout va avant qu'il ne soit écrit à ce descripteur de fichier initial (file), qui est vraiment tout ce qu'il est.

2
répondu Vex 2009-06-11 10:40:47

@Hobo utiliser freopen() , il réutilise les flux à ouvrir le fichier spécifié par filename ou de changer son mode d'accès. Si un nouveau nom de fichier est spécifié, la fonction essaie d'abord de fermer un fichier déjà associé à stream (troisième paramètre) et le dissocie. Ensuite, indépendamment du fait que ce flux ait été fermé avec succès ou non, freopen ouvre le fichier spécifié par filename et l'associe avec le flux comme fopen le ferait en utilisant mode spécifié.

si un binaire tiers génère des logs, nous devons écrire un wrapper qui fera tourner les logs, et thirdparty s'exécutera dans le thread proxyrun comme ci-dessous.

#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;
}
2
répondu Yogesh 2012-11-05 10:29:21

au lieu de rediriger vers un fichier, vous pouvez le diriger vers un programme qui tourne automatiquement le fichier en clôture, le déplacer et d'en ouvrir un nouveau à chaque fois qu'il devient trop grand.

1
répondu Arthur Ulfeldt 2009-06-12 17:18:31

j'ai eu un problème similaire et j'ai été incapable de faire un "tail-f" sur la sortie d'un script qui a été exécuté à partir de cron:

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

Je l'ai corrigé en changeant la redirection stderr:

    * * * * * my_script >> /var/log/my_script.log 2>/var/log/my_script.err
1
répondu lfjeff 2016-11-27 06:23:09