파이프 쓰기를 방해하지 않고 파일 비우기 않는 것 같습니다. 그렇게 할 방법이 있습니까? 최신 정보 출력을

출력을 로그 파일로 리디렉션하는 프로그램이 있습니다.

./my_app > log

나는 때때로 (필요에 따라) 로그를 지우고 (즉, 비우고) 다음과 같은 다양한 것을 시도했다.

cat "" > log

그러나 항상 원래 파이프가 중단되고 프로그램이 더 이상 출력을 로그 파일로 리디렉션하지 않는 것 같습니다.

그렇게 할 방법이 있습니까?

최신 정보

출력을 생성하는 응용 프로그램을 수정할 수 없습니다. 그것은 stdout에 뱉어 내고 필요할 때 검사하고 필요할 때 지울 수 있도록 로그에 저장하고 싶습니다. 그러나 응용 프로그램을 다시 시작할 필요는 없습니다.



답변

이 문제의 다른 형태는 로그가 주기적으로 순환되는 오래 실행되는 응용 프로그램에서 발생합니다. mv log.txt log.1실제 로그가 발생하기 전에 원본 로그 (예 :)를 이동하고 동일한 이름의 파일로 즉시 바꾸어도 프로세스가 파일을 열린 상태로 유지하면 쓰기가 끝날 log.1수 있습니다. 열린 아이 노드) 또는 아무것도 아닙니다.

이를 처리하는 일반적인 방법 (시스템 로거 자체가이 방식으로 작동 함)은 로그를 닫았다가 다시 여는 프로세스에서 신호 처리기를 구현하는 것입니다. 그런 다음 로그를 이동하거나 지우려면 (삭제하여) 즉시 해당 신호를 프로세스에 보냅니다.

다음은 bash에 대한 간단한 데모입니다. 저의 셸 기술을 용서하십시오 (그러나 모범 사례 등을 위해이를 편집하려는 경우 먼저 기능을 이해하고 수정 하기 전에 수정 사항을 테스트하십시오 ).

#!/bin/bash

trap sighandler INT

function sighandler () {
    touch log.txt
    exec &> log.txt
}

echo $BASHPID
exec &> log.txt

count=0;
while [ $count -lt 60 ]; do
    echo "$BASHPID Count is now $count"
    sleep 2
    ((count++))
done          

백그라운드로 포크하여 시작하십시오.

> ./test.sh &
12356

PID를 터미널에보고 한 다음로 로그를 시작합니다 log.txt. 이제 2 분 동안 놀 수 있습니다. 몇 초 기다렸다가 시도하십시오.

> mv log.txt log.1 && kill -s 2 12356

kill -2 12356여기서도 평범한 것이 당신을 위해 일할 수도 있습니다. 신호 2는 SIGINT (Ctrl-C도 수행하므로 포 그라운드에서 시도하고 다른 터미널에서 로그 파일을 이동 또는 제거 할 수 있음)이며 trap트랩해야합니다. 확인하다;

> cat log.1
12356 Count is now 0
12356 Count is now 1
12356 Count is now 2
12356 Count is now 3
12356 Count is now 4
12356 Count is now 5
12356 Count is now 6
12356 Count is now 7
12356 Count is now 8
12356 Count is now 9
12356 Count is now 10
12356 Count is now 11
12356 Count is now 12
12356 Count is now 13
12356 Count is now 14

이제 log.txt우리가 그것을 옮겼 는데도 여전히 쓰고 있는지 확인하십시오 .

> cat log.txt
12356 Count is now 15
12356 Count is now 16
12356 Count is now 17
12356 Count is now 18
12356 Count is now 19
12356 Count is now 20
12356 Count is now 21

중단 된 위치에서 계속 올바르게 진행됩니다. 기록을 유지하지 않으려면 삭제하여 로그를 지우십시오.

> rm -f log.txt && kill -s 2 12356

검사:

> cat log.txt
12356 Count is now 29
12356 Count is now 30
12356 Count is now 31
12356 Count is now 32
12356 Count is now 33
12356 Count is now 34
12356 Count is now 35
12356 Count is now 36

계속 진행중.

불행히도, 실행 된 서브 프로세스에 대해 쉘 스크립트에서이를 수행 할 수 없습니다. 포 그라운드에 있으면 bash의 자체 신호 핸들러 trap가 일시 중단되고 백그라운드로 분기하면 재 할당 할 수 없기 때문입니다. 산출. 즉, 응용 프로그램에서 구현해야합니다.

하나…

응용 프로그램을 수정할 수없는 경우 (예 : 작성하지 않았기 때문에) 중개자로 사용할 수 있는 CLI 유틸리티가 있습니다. 로그의 파이프 역할을하는 스크립트에서 간단한 버전을 구현할 수도 있습니다.

#!/bin/bash

trap sighandler INT

function sighandler () {
    touch log.txt
    exec 1> log.txt
}

echo "$0 $BASHPID"
exec 1> log.txt

count=0;
while read; do
    echo $REPLY
done  

이것을 호출합시다 pipetrap.sh. 이제 테스트 할 별도의 프로그램이 필요합니다. 로깅하려는 응용 프로그램을 모방합니다.

#!/bin/bash

count=0
while [ $count -lt 60 ]; do
    echo "$BASHPID Count is now $count"
    sleep 2
    ((count++))
done           

그것은 될 것입니다 test.sh:

> (./test.sh | ./pipetrap.sh) &
./pipetrap.sh 15859

별도의 PID가있는 두 개의 개별 프로세스입니다. 를 test.sh통해 유입되는의 출력 을 지우려면 pipetrap.sh:

> rm -f log.txt && kill -s 2 15859

검사:

>cat log.txt
15858 Count is now 6
15858 Count is now 7
15858 Count is now 8

15858“이 test.sh(가) 계속 실행 중이고 출력이 기록되고 있습니다. 이 경우 응용 프로그램을 수정할 필요가 없습니다.


답변

TL; DR

추가 모드 에서 로그 파일을여십시오 .

cmd >> log

그런 다음 다음을 사용하여 안전하게자를 수 있습니다.

: > log

세부

Bourne과 같은 쉘을 사용하면 파일을 쓰기 위해 열 수있는 3 가지 주요 방법이 있습니다. 에서 쓰기 전용 ( >), 읽기 + 쓰기 ( <>) 또는 APPEND (및 쓰기 전용 >>) 모드.

처음 두에서, 커널은 (당신에 의해, 내 말은 현재 위치를 기억 열린 파일 설명 중복 또는 당신이 파일을 연 하나에서 분기하여 상속 한 모든 파일 기술자에 의해 공유는 등)에 있습니다 파일.

할 때 :

cmd > log

log의 stdout에 대해 쉘에 의해 쓰기 전용 모드로 열립니다 cmd.

cmdstdout에 쓸 때 (쉘과 가능한 모든 자식에 의해 생성 된 초기 프로세스) 해당 파일에서 공유 하는 열린 파일 설명에 의해 유지되는 현재 커서 위치에 씁니다 .

예를 들어, cmd처음에을 쓰는 경우 zzz위치는 파일에 바이트 오프셋 4가되고 다음에 해당 파일 cmd또는 자식이 파일에 쓰면 파일이 간격에 따라 커 졌거나 줄어든 지 여부에 관계없이 데이터가 기록됩니다. .

예를 들어 파일이 잘린 경우 파일이 축소 된 경우

: > log

그리고 cmdwrites xx, 그것들 xx은 offset에 쓰여질 4것이고, 처음 3 문자는 NUL 문자로 대체 될 것입니다.

$ exec 3> log # open file on fd 3.
$ printf zzz >&3
$ od -c log
0000000   z   z   z
0000003
$ printf aaaa >> log # other open file description -> different cursor
$ od -c log
0000000   z   z   z   a   a   a   a
0000007
$ printf bb >&3 # still write at the original position
$ od -c log
0000000   z   z   z   b   b   a   a
0000007
$ : > log
$ wc log
0 0 0 log
$ printf x >&3
$ od -c log
0000000  \0  \0  \0  \0  \0   x
0000006

즉, 쓰기 전용 모드로 열린 파일 (및 읽기 + 쓰기 와 동일한 파일)은 파일 디스크립터가 파일에서 열린 프로세스와 같이 파일의 시작 부분에 NUL 문자가 남는 것처럼 잘라낼 수 없습니다 . 파일 (OS / X를 제외하고 일반적으로 디스크의 공간을 차지하지 않고 드문 파일이 됨)

대신 (대부분의 응용 프로그램이 로그 파일에 쓸 때 그렇게하는 것을 알 수 있습니다) 추가 모드 에서 파일을 열어야 합니다.

cmd >> log

또는

: > log && cmd >> log

빈 파일에서 시작하려면

추가 모드에서는 마지막 쓰기 위치에 관계없이 파일 끝에 모든 쓰기가 수행됩니다.

$ exec 4>> log
$ printf aa >&4
$ printf x >> log
$ printf bb >&4
$ od -c log
0000000   a   a   x   b   b
0000005
$ : > log
$ printf cc >&4
$ od -c log
0000000   c   c
0000002

두 프로세스가 파일을 실수로 (예 : 같은 데몬의 두 인스턴스를 시작한 것처럼) 열어 놓은 것처럼 출력이 서로 덮어 쓰지 않는 것이 더 안전합니다.

최신 Linux 버전에서는 다음을보고 현재 위치와 파일 디스크립터가 추가 모드 에서 열려 있는지 확인할 수 있습니다 /proc/<pid>/fdinfo/<fd>.

$ cat /proc/self/fdinfo/4
pos:        2
flags:      0102001

또는

$ lsof +f G -p "$$" -ad 4
COMMAND  PID USER   FD   TYPE  FILE-FLAG DEVICE SIZE/OFF     NODE NAME
zsh     4870 root    4w   REG 0x8401;0x0 252,18        2 59431479 /home/chazelas/log
~# lsof +f g -p "$$" -ad 4
COMMAND  PID USER   FD   TYPE FILE-FLAG DEVICE SIZE/OFF     NODE NAME
zsh     4870 root    4w   REG   W,AP,LG 252,18        2 59431479 /home/chazelas/log

이러한 플래그 는 시스템 호출에 전달 된 O …_ 플래그에 해당 open합니다.

$ gcc -E - <<< $'#include <fcntl.h>\nO_APPEND O_WRONLY' | tail -n1
02000 01

( O_APPEND0x400 또는 8 진수 02000 임)

쉘의는 그래서 >>사용하여 파일을 엽니 다 O_WRONLY|O_APPEND하면서 (그리고 0,100,000 여기이 질문에 관련이 없습니다 O_LARGEFILE입니다) >입니다 O_WRONLY만 (그리고 <>입니다 O_RDWR만).

당신이 할 경우 :

sudo lsof -nP +f g | grep ,AP

로 열린 파일을 검색하기 위해 O_APPEND현재 시스템에 쓰기 위해 열려있는 대부분의 로그 파일을 찾을 수 있습니다.


답변

내가 올바르게 이해하고 있다면 tee합리적인 접근법처럼 보입니다.

$ ./myapp-that-echoes-the-date-every-second | tee log > /dev/null &
[1] 20519
$ head log
Thu Apr  3 11:29:34 EDT 2014
Thu Apr  3 11:29:35 EDT 2014
Thu Apr  3 11:29:36 EDT 2014
$ > log
$ head log
Thu Apr  3 11:29:40 EDT 2014
Thu Apr  3 11:29:41 EDT 2014
Thu Apr  3 11:29:42 EDT 2014

답변

빠른 솔루션으로 회전과 함께 로그를 사용할 수 있습니다 (예 : 매일 회전).

date=`date +%Y%m%d`
LOGFILE=/home/log$date.log

로깅을 리디렉션 ./my_app >> log$date.log


답변

이것은 syslog로 오랫동안 해결 된 문제 (모든 변종에서)이지만 최소한의 노력으로 특정 문제를 해결할 수있는 두 가지 도구가 있습니다.

이식성이 높고 기능이 덜한 첫 번째 솔루션은 로거입니다 (모든 관리자 도구 상자에 있어야 함). 표준 입력을 syslog에 복사하는 간단한 유틸리티입니다. (벅을 통과시키고 파일 회전을 logrotate 및 syslog의 문제로 만들기)

두 번째로 더 우아하지만 이식성이 낮은 솔루션은 syslog-ng이며 표준 syslog 소켓에서 로그 메시지를 수락하는 것 외에도 로거를 통해 출력이 필터링되는 프로그램을 실행할 수 있습니다. (이 기능은 아직 사용하지 않았지만 원하는 기능에 완벽하게 보입니다.)