날짜가 지정되지 않은 줄을 포함하는 여러 로그 파일을 날짜별로 병합 (예 : 스택 추적)


6

로그 파일, 즉 시간별로 정렬되었지만 첫 줄만 시간이 있고 나머지 줄은없는 여러 줄이있는 파일을 병합하는 방법은 무엇입니까?

log1

01:02:03.6497,2224,0022 foo
foo1
2foo
foo3
01:04:03.6497,2224,0022 bar
1bar
bar2
3bar

log2

01:03:03.6497,2224,0022 FOO
FOO1
2FOO
FOO3

예상 결과

01:02:03.6497,2224,0022 foo
foo1
2foo
foo3
01:03:03.6497,2224,0022 FOO
FOO1
2FOO
FOO3
01:04:03.6497,2224,0022 bar
1bar
bar2
3bar

타임 스탬프가 아닌 라인이 숫자로 시작 sort -nm log1 log2하지 않았다면 간단 합니다.

유닉스 / 리눅스 cmd 라인에서 작업을 완료하는 쉬운 방법이 있습니까?

편집 이러한 로그 파일은 종종 기가 바이트 단위이므로 (이미 정렬 된) 로그 파일을 다시 정렬하지 않고 파일을 메모리에 완전히로드하지 않고 병합해야합니다.


이것이 실제 UNIX입니까, 아니면 Linux를 의미합니까? GNU 도구가 있습니까?
terdon

답변:


10

교활한. date배열을 사용 하고 bash를 사용하는 것이 가능하지만 실제로는 실제 프로그래밍 언어의 이점이 있습니다. 예를 들어 Perl에서 :

$ perl -ne '$d=$1 if /(.+?),/; $k{$d}.=$_; END{print $k{$_} for sort keys(%k);}' log*
01:02:03.6497,2224,0022 foo
foo1
2foo
foo3
01:03:03.6497,2224,0022 FOO
FOO1
2FOO
FOO3
01:04:03.6497,2224,0022 bar
1bar
bar2
3bar

다음은 주석 처리 된 스크립트로 압축되지 않은 것과 같습니다.

#!/usr/bin/env perl

## Read each input line, saving it 
## as $_. This while loop is equivalent
## to perl -ne 
while (<>) {
    ## If this line has a comma
    if (/(.+?),/) {
        ## Save everything up to the 1st 
        ## comma as $date
        $date=$1;
    }
    ## Add the current line to the %k hash.
    ## The hash's keys are the dates and the 
    ## contents are the lines.
    $k{$date}.=$_;
}

## Get the sorted list of hash keys
@dates=sort(keys(%k));
## Now that we have them sorted, 
## print each set of lines.
foreach $date (@dates) {
    print "$k{$date}";
}

이 모든 일의 라인과 고 가정합니다 에만 날짜 라인에 쉼표가 포함되어 있습니다. 그렇지 않은 경우 대신 다음을 사용할 수 있습니다.

perl -ne '$d=$1 if /^(\d+:\d+:\d+\.\d+),/; $k{$d}.=$_; END{print $k{$_} for sort keys(%k);}' log*

위의 방법은 파일의 전체 내용을 메모리에 보관해야합니다. 그것이 문제라면, 여기에없는 것이 있습니다 :

$ perl -pe 's/\n/\0/; s/^/\n/ if /^\d+:\d+:\d+\.\d+/' log* | 
    sort -n | perl -lne 's/\0/\n/g; printf'
01:02:03.6497,2224,0022 foo
foo1
2foo
foo3    
01:03:03.6497,2224,0022 FOO
FOO1
2FOO
FOO3    
01:04:03.6497,2224,0022 bar
1bar
bar2
3bar

이것은 줄 바꿈을 바꾸어 연속 타임 스탬프 사이의 모든 줄을 한 줄로 \0만듭니다. 이는 전달 sort하고 tr다시 라인을 얻을 수 있습니다.


OP에서 매우 정확하게 지적했듯이 위의 모든 솔루션을 수정해야하며 파일을 병합 할 수 있다는 점을 고려하지 마십시오. 다음은 다른 파일과 달리 두 파일에서만 작동하는 파일입니다.

$ sort -m <(perl -pe 's/\n/\0/; s/^/\n/ if /^\d+:\d+:\d+\.\d+/' log1) \
            <(perl -pe 's/\n/\0/; s/^/\n/ if /^\d+:\d+:\d+\.\d+/' log2) | 
    perl -lne 's/[\0\r]/\n/g; printf'

그리고 perl 명령을 별명으로 저장하면 다음을 얻을 수 있습니다.

$ alias a="perl -pe 's/\n/\0/; s/^/\n/ if /^\d+:\d+:\d+\.\d+/'"
$ sort -m <(a log1) <(a log2) | perl -lne 's/[\0\r]/\n/g; printf'

이 문제는 모든 파일의 내용을 메모리에 먼저로드한다는 것입니다. 파일이 이미 정렬되어 있으므로 이것을 피하고 스트리밍 방식으로 수행해야합니다 (Perl과 같은 범용 언어를 사용하면 익숙하지 않으므로 쉽지 않습니다)
Eugene Beresovsky

@EugeneBeresovksy 어떻게 보지 못합니다. 파일이 정렬되지 않았으므로 전체 문제입니다. 그들은 각 파일에 분류하고, 그러나, 당신은 찾을 수 line1line3fileA있는 동안 line2이다 fileB. 임시 파일을 사용하거나 메모리에 저장하지 않고 어떻게 정렬 할 수 있는지 모르겠습니다. tmp 파일을 원한다면 알려 주시면 예제를 드리겠습니다.
terdon

그렇게 복잡하지는 않습니다. 모든 파일의 첫 번째 줄을 읽은 다음 가장 오래된 줄과 그 줄을 따르고 잊어 버린 파일의 오래된 여러 줄을 출력합니다. 그런 다음 가장 오래된 행이있는 파일로 이동하여 동일한 작업을 수행하십시오. 소모 될 때까지 파일간에 프로세스를 반복합니다. 그 당신에게 이해가되지 않는 경우,이 같은 뭔가가 확인할 수 있어야합니다 예를 들어, 모니터링에 의해 가능 ps -eo comm,vsz|grep perl펄이 대를 실행하는 동안 sort -nm log*. 총 650MB의 3 개의 파일로 시도했습니다. 귀하의 솔루션에 대한 최대 mem : 863 MB, 종류 :8 MB
Eugene Beresovsky

@EugeneBeresovksy 예, 말했듯이 임시 파일을 사용하는 것이 가능합니다 sort. 나는 당신이 묘사하고 있지만 전체 정렬 알고리즘을 구현하는 것이이 사이트의 범위를 벗어난 것을 알았습니다.
terdon

1) 정렬 알고리즘의 구현을 요구하지 않았습니다. 2) 이미 정렬 된 파일을 병합하기 위해 마지막 주석에서 설명한 것처럼 전체 정렬이 필요하지 않습니다. 3) 정렬이 실제로 필요하더라도, 누군가가 바퀴를 다시 발명해야한다는 의미는 아닙니다. 예를 들어 유닉스 명령에 약간의 알려진 플래그가있을 수 있습니다.
Eugene Beresovsky

1

그것을하는 한 가지 방법 (개행을 대체하기 위해 @terdon 덕분에 아이디어) :

  1. 각 입력 파일에서 줄 바꾸기를 예를 들어 NUL로 바꾸어 모든 여러 줄을 한 줄로 연결하십시오.
  2. A는 수행 sort -m교체 된 파일에
  3. NUL을 개행으로 다시 대체

여러 줄로 된 연결이 두 번 이상 사용 alias되므로 멀리 두십시오.

alias a="awk '{ if (match(\$0, /^[0-9]{2}:[0-9]{2}:[0-9]{2}\\./, _))\
    { if (NR == 1) printf \"%s\", \$0; else printf \"\\n%s\", \$0 }\
    else printf \"\\0%s\", \$0 } END { print \"\" }'"

위의 별칭을 사용하는 merge 명령은 다음과 같습니다.

sort -m <(a log1) <(a log2) | tr '\0' '\n'

쉘 스크립트로

이렇게 사용하려면

merge-logs log1 log2

쉘 스크립트에 넣었습니다.

x=""
for f in "$@";
do
 x="$x <(awk '{ if (match(\$0, /^[0-9]{2}:[0-9]{2}:[0-9]{2}\\./, _)) { if (NR == 1) printf \"%s\", \$0; else printf \"\\n%s\", \$0 } else printf \"\\0%s\", \$0 } END { print \"\" }' $f)"
done

eval "sort -m $x | tr '\0' '\n'"

내가 악의에 의지하지 않고 다양한 수의 로그 파일을 제공 할 수 있는지 확실하지 않습니다 eval.


좋아요, +1 나는 그것이 awk불필요하게 복잡해 보인다고 말해야하지만 . 별명에 훨씬 짧은 펄 버전을 사용하지 않으시겠습니까?
terdon

나는 당신의 perl을 사용하려고 시도했지만 문제는 항상 처음에 줄 바꿈을 추가한다는 것입니다. 동일한 문제 btw의 awk해결책 은 솔루션을 그렇게 복잡 하게 만드는 것 입니다. 당신의 펄은 내가 비슷하게 보일 것입니다. 내가 awk로 바꾼 이유는 내가 perlist가 아니기 때문입니다.
Eugene Beresovsky

아, 빈 줄을 제거를 얻기 위해, 그냥 통과 grep .: perl -ne 's/\n/\0/; s/^/\n/ if /\d+:\d+:\d+\.\d+/ ; print' log* | tr -s '\0' '\n' | grep .심지어 awk 'NR>1'.
terdon

당신은 남자입니다. 당신은 모든 좋은 아이디어를 얻었습니다. 단, grep .원래 존재했던 빈 줄도 삼킨다. awk그러나 귀하의 안전은 그대로 tail -n+2입니다.
Eugene Beresovsky

당신의 펄은 내 awk보다 훨씬 빠릅니다. 그러나 tr -s현재 펄 솔루션과 함께 제공되는 것은 원본에 있던 빈 줄을 제거합니다. 일반적으로 충분하지만 결과는 완벽한 병합이 아닙니다.
Eugene Beresovsky

0

Java를 사용하는 것이 옵션이라면 log-merger를 사용해보십시오 .

java -jar log-merger-0.0.3-jar-with-dependencies.jar -f 1 -tf "HH:MM:ss.SSS" -d "," -i log1,log2
01:02:03.6497,2224,0022 foo
foo1
2foo
foo3
01:03:03.6497,2224,0022 FOO
FOO1
2FOO
FOO3
01:04:03.6497,2224,0022 bar
1bar
bar2
3bar

Java 만 시작하는 데 오랜 시간이 걸리지 않으면 즉시 적절한 JVM 언어로 쉘 스크립트 작성으로 전환합니다 !
Eugene Beresovsky
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.