40

ログ ファイルにリダイレクトしている stdout に大量のデータを書き込むプロセスがあります。時々現在のファイルを新しい名前にコピーして切り詰めることで、ファイルのサイズを制限したいと思います。

ファイルを切り捨てる私の通常のテクニック

cp /dev/null file

おそらくプロセスがそれを使用しているため、機能しません。

ファイルを切り捨てる方法はありますか? または、それを削除して、何らかの方法でプロセスの 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
4

13 に答える 13

33

coreutils 7.0 の時点で、truncateコマンドがあります。

于 2010-07-16T11:03:40.250 に答える
32

これらの再成長したファイルの興味深い点は、ファイルをコピー/dev/nullして切り捨てた後、最初の 128 KB 程度がすべてゼロになることです。これは、ファイルが長さ 0 に切り詰められているために発生しますが、アプリケーションのファイル記述子は最後の書き込みの直後を指しています。再度書き込みを行うと、ファイル システムはファイルの先頭をすべてゼロ バイトとして扱いますが、実際にはゼロをディスクに書き込みません。

理想的には、アプリケーションのベンダーにO_APPENDフラグ付きのログ ファイルを開くように依頼する必要があります。これは、ファイルを切り捨てた後、次の書き込みで暗黙的にファイルの最後までシークし (オフセット ゼロに戻ることを意味します)、新しい情報を書き込むことを意味します。


このコードは、標準出力を調整してO_APPENDモードにしてから、その引数で指定されたコマンドを呼び出します ( nicenice レベルを調整した後に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 ') コードではなく追加リダイレクトを使用してください。これは、ある特定の手法を他の (有効な) 目的に使用する場合、それを別の手法に適応させることは、たとえ機能していても、必ずしも最も単純なメカニズムではないことを示しています。

于 2009-06-12T02:40:32.627 に答える
14

> の代わりに >> を使用して出力をリダイレクトします。これにより、ファイルが元のサイズに戻ることなく、ファイルを切り詰めることができます。また、STDERR (2>&1) をリダイレクトすることを忘れないでください。

したがって、最終結果は次のようになります。myprogram >> myprogram.log 2>&1 &

于 2013-05-23T17:46:49.547 に答える
10

試してみてください> file


コメントに関する更新:私にとってはうまく機能します:

robert@rm:~> echo "content" > test-file
robert@rm:~> cat test-file 
content
robert@rm:~> > test-file
robert@rm:~> cat test-file 
于 2009-06-11T09:58:54.007 に答える
6

Linux (実際にはすべてのユニシー) では、ファイルを開くとファイルが作成され、ファイルへの参照が何も保持されていない場合は削除されます。この場合、それを開いたプログラムとそれが開かれたディレクトリは、ファイルへの参照を保持しています。cp プログラムがファイルに書き込みたい場合、ディレクトリからそのファイルへの参照を取得し、ディレクトリに格納されているメタデータに長さゼロを書き込み (これは少し単純化されています)、ハンドルを放棄します。次に、元のファイル ハンドルを保持している元のプログラムが、さらにデータをファイルに書き込み、必要な長さを保存します。

ディレクトリからファイルを削除したとしても、他のプログラムがそれを参照する方法がない場合でも、プログラムはデータを書き込み続けます(そしてディスクスペースを使い果たします)。

つまり、プログラムがファイルへの参照 (ハンドル) を取得すると、それを変更することはできません。

理論的には、LD_LIBRARY_PATH を設定して、すべてのファイル アクセス システム コールをインターセプトするプログラムを含めることにより、プログラムの動作を変更する方法があります。名前は思い出せませんが、どこかでこのようなものを見た記憶があります。

于 2009-06-12T02:15:41.267 に答える
5

split(1)GNU Coreutils の一部である utility を見てください。

于 2009-06-11T10:00:52.067 に答える
3

ファイルが使用されているときに、ファイルなどを無効にしようとすると、ログファイルに書き込んでいるアプリが「混乱」し、その後何もログに記録されない場合があります。

私がやろうとしているのは、ファイルにリダイレクトしたり、入力を取得してローリングファイルに書き込んだりするプロセスなどにリダイレクトするのではなく、そのログに一種のプロキシ/フィルターを設定することです。

たぶんそれはスクリプトによって行うことができます、さもなければあなたはそのための簡単なアプリ(javaか何か他のもの)を書くことができます。アプリのパフォーマンスへの影響は非常に小さいはずですが、いくつかのテストを実行する必要があります。

ところで、あなたのアプリは、スタンドアロンのWebアプリですか...?たぶん、調査すべき他のオプションがあります。

編集:私が個人的に使用したことのないAppend Redirection Operator >>もありますが、ファイルがロックされない可能性があります。

于 2009-06-11T10:34:43.540 に答える
3

最新のものをダウンロードしてコンパイルしたcoreutilsので、truncate利用できるようになりました。

./configureと実行されましmakeたが、実行されませんでしたmake install

コンパイルされたすべてのユーティリティが「src」フォルダに表示されます。

走った

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

1.7 GB のログ ファイル。

を使用したときにリストされたサイズは変更されませんでしたls -lが、すべてのディスク領域が解放さ/varれました。

「切り捨て」についてのヒントをありがとう!

于 2011-06-20T18:30:03.857 に答える
2

サードパーティ製品へのSIGHUPなどのシグナルの動作をチェックして、新しいファイルのログ記録を開始するかどうかを確認しましたか?まず、古いファイルを永続的な名前に移動します。

kill-HUP[プロセスID]

そして、それは再び書き始めるでしょう。

あるいは(Billyが提案したように)、アプリケーションからの出力を、multilogなどのロギングプログラムまたはApacheで一般的に使用されるcronologとして知られるロギングプログラムにリダイレクトすることもできます。そうすれば、最初のファイル記述子(file)に書き込まれる前に、すべてがどこに行くかをよりきめ細かく制御できるようになります。これが実際のすべてです。

于 2009-06-11T10:40:47.613 に答える
2

ファイルにリダイレクトする代わりに、ファイルを閉じて移動し、ファイルが大きくなりすぎるたびに新しいファイルを開くことで、ファイルを自動的にローテーションするプログラムにパイプすることができます。

于 2009-06-12T17:18:31.580 に答える
2

@Hobo はfreopen()を使用します。ストリームを再利用して、ファイル名で指定されたファイルを開くか、アクセス モードを変更します。新しいファイル名が指定された場合、関数は最初にストリーム (3 番目のパラメーター) に既に関連付けられているファイルを閉じようとし、その関連付けを解除します。次に、そのストリームが正常に閉じられたかどうかに関係なく、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;
}
于 2012-11-05T10:29:21.113 に答える