OS X/Linux 단일 라인/텍스트 파일에서 가장 큰 중복 라인 그룹을 찾는 스크립트?

OS X/Linux 단일 라인/텍스트 파일에서 가장 큰 중복 라인 그룹을 찾는 스크립트?

무한 재귀가 발생하고 결국 스택이 너무 깊어지면 종료되는 실행 추적이 포함된 로그가 있습니다. 더 큰 라인 블록 내에 충분한 라인과 유효한 재귀가 포함되어 있어 반복되는 가장 큰 블록을 식별하기 어렵습니다. 이 결정을 내리기 위해 일부 행을 필터링해야 하는 고유한 사항은 없습니다.

파일 이름/경로 이름이 주어지면 최대 행 집합만 출력하는 좋은 한 줄/스크립트(POSIX/OS X, Linux 및 OS X에서 작동하는 것이 바람직함)는 무엇입니까?

설명: 내 예에서 로그 파일에는 432003행과 80M이 있습니다.

$ wc -l long_log.txt 
432003 long_log.txt
$ du -sm long_log.txt
80  long_log.txt

비슷한 입력 파일을 만들려면 이것을 시도해 보십시오. 게시물 주셔서 감사합니다.여기임의의 단어가 포함된 파일을 만드는 방법입니다.

ruby -e 'a=STDIN.readlines;200000.times do;b=[];22.times do; b << a[rand(a.size)].chomp end; puts b.join(" "); end' < /usr/share/dict/words > head.txt
ruby -e 'a=STDIN.readlines;2.times do;b=[];22.times do; b << a[rand(a.size)].chomp end; puts b.join(" "); end' < /usr/share/dict/words > recurrence1.txt
ruby -e 'a=STDIN.readlines;20.times do;b=[];22.times do; b << a[rand(a.size)].chomp end; puts b.join(" "); end' < /usr/share/dict/words > recurrence2.txt
ruby -e 'a=STDIN.readlines;200000.times do;b=[];22.times do; b << a[rand(a.size)].chomp end; puts b.join(" "); end' < /usr/share/dict/words > tail.txt
cat head.txt recurrence1.txt recurrence1.txt recurrence2.txt recurrence1.txt recurrence1.txt recurrence2.txt recurrence1.txt tail.txt > log.txt
cat recurrence1.txt recurrence1.txt recurrence2.txt > expected.txt

결과적으로:

$ wc -l log.txt 
400050 log.txt
$ du -sm log.txt
89  log.txt

그러면 다음을 수행할 수 있습니다.

$ recurrence log.txt > actual.txt
$ diff actual.txt expected.txt
$

동일한 길이의 다른 블록을 식별하는 경우에도 괜찮습니다.

$ cat recurrence1.txt recurrence2.txt recurrence1.txt recurrence1.txt recurrence2.txt recurrence1.txt > expected2.txt
$ diff actual.txt expected2.txt
$

2.6GHz 쿼드 코어 Intel Core i7과 OS X/Linux의 16GB RAM을 사용하여 10초 이내에 예상한 결과를 찾았으면 좋겠습니다.

답변1

해결책은 다음과 같습니다.TXR 언어.

@(next :args)
@(bind rangelim nil)
@(block)
@  (cases)
@filename
@    (maybe)
@rlim
@      (set rangelim @(int-str rlim))
@    (end)
@    (eof)
@  (or)
@    (output)
arguments are: filename [ range-limit ]
@    (end)
@    (fail)
@  (end)
@(end)
@(do
   (defun prefix-match (list0 list1)
     (let ((c 0))
       (each ((l0 list0)
              (l1 list1))
         (if (not (equal l0 l1))
           (return c))
         (inc c))
       c))

   (defun line-stream (s)
     (let (li) (gen (set li (get-line s)) li)))

   (let* ((s (line-stream (open-file filename "r")))
          (lim rangelim)
          (s* (if lim s nil))
          (h (hash :equal-based))
          (max-len 0)
          (max-line nil))
     (for ((ln 1)) (s) ((set s (rest s)) (inc ln))
       (let ((li (first s)))
         (let ((po (gethash h li))) ;; prior occurences
           (each ((line [mapcar car po])
                  (pos [mapcar cdr po]))
             (let ((ml (prefix-match pos s)))
               (cond ((and 
                        (= ml (- ln line))
                        (> ml max-len))
                      (set max-len ml)
                      (set max-line line))))))
         (pushhash h li (cons ln s))
         (if (and lim (> ln lim))
           (let* ((oldli (first s*))
                  (po (gethash h oldli))
                  (po* (remove-if (op eq s* (cdr @1)) po)))
             (if po*
               (sethash h oldli po*)
               (remhash h oldli))
             (set s* (cdr s*))))))
     (if max-line
       (format t "~a line(s) starting at line ~a\n" max-len max-line)
       (format t "no repeated blocks\n"))))

이 프로그램은 거의 전적으로 TXR에 내장된 Lisp 방언으로 구성됩니다. 여기서 접근 방식은 파일의 각 줄을 해시 테이블에 저장하는 것입니다. 파일의 어느 지점에서나 우리는 해시 테이블에 "이 줄을 이전에 어디서 본 적이 있습니까?"라고 물을 수 있습니다. 그렇다면 해당 위치에서 시작하는 파일과 현재 위치에서 시작하는 줄을 비교할 수 있습니다. 일치 항목이 이전 위치에서 현재 위치까지 확장되면 이는 연속 일치가 있음을 의미합니다. 이전 위치에서 현재 줄까지의 모든 N 줄은 현재 줄부터 시작하여 N 줄과 일치합니다. 우리가 해야 할 일은 모든 후보 위치 중에서 가장 긴 일치를 생성하는 위치를 찾는 것입니다. (연결이 있는 경우 첫 번째 연결만 보고됩니다.)

보세요, Xorg 로그 파일에 두 줄이 반복적으로 나열되어 있습니다.

$ txr longseq.txr  /var/log/Xorg.0.log
2 line(s) starting at line 168

168번째 줄에는 무엇이 있나요? 이 네 줄은 다음과 같습니다.

[    19.286] (**) VBoxVideo(0):  Built-in mode "VBoxDynamicMode": 56.9 MHz (scaled from 0.0 MHz), 44.3 kHz, 60.0 Hz
[    19.286] (II) VBoxVideo(0): Modeline "VBoxDynamicMode"x0.0   56.94  1280 1282 1284 1286  732 734 736 738 (44.3 kHz)
[    19.286] (**) VBoxVideo(0):  Built-in mode "VBoxDynamicMode": 56.9 MHz (scaled from 0.0 MHz), 44.3 kHz, 60.0 Hz
[    19.286] (II) VBoxVideo(0): Modeline "VBoxDynamicMode"x0.0   56.94  1280 1282 1284 1286  732 734 736 738 (44.3 kHz)

반면에 비밀번호 파일은 모두 고유합니다.

$ txr longseq.txr  /etc/passwd
no repeated blocks

추가 두 번째 인수를 사용하여 프로그램 속도를 높일 수 있습니다. 가장 긴 반복 시퀀스가 ​​50줄을 넘지 않는다는 것을 알고 있다면 이를 지정할 수 있습니다. 그러면 프로그램은 50줄 이상을 역추적하지 않습니다. 또한 메모리 사용량은 파일 크기가 아닌 범위 크기에 비례하므로 반대 방향으로 승리합니다.

답변2

특히 중복이 많은 경우 큰 로그에서 큰 중복 블록을 찾는 가장 빠르고 쉬운 방법은 다음과 같습니다.

sort long_log.txt | uniq -c | sort -k1n

(답변을 토대로 정리했습니다.여기그리고여기.)

long_log.txt는 54초가 걸렸는데, 이는 더 반복되는 작업이었습니다. 이는 제가 요청한 작업을 정확히 수행하는 스크립트의 문제인 것 같습니다. 반면 무작위로 생성된 log.txt는 47초가 걸렸습니다.

행은 순서가 지정되지 않았으며 재귀 내에 재귀가 있는 경우 행을 개별적으로 그룹화할 수 있지만(개수가 더 많을 수 있음) 이 방법의 데이터를 사용한 다음 로그로 돌아가서 관련 항목을 찾아 추출할 수도 있습니다. 부속.

.bashrc명령은 함수로 / 에 넣을 수 있습니다 ..bash_profile

recurrence() {
  sort "$1" | uniq -c | sort -k1n
}

이렇게 하면 다음과 같이 호출할 수 있습니다.

recurrence long_log.txt

답변3

이것이 bash의 솔루션입니다. 사실은 내가스크립트가 있습니다;그러나 이것이 유일한 것입니다:

find $PWD -regextype posix-extended -iregex '.*\.(php|pl)$' -type f | xargs wc -L 2> /dev/null | grep -v 'total' | sort -nrk1 | head -n 30 | awk 'BEGIN { printf "\n%-15s%s\n", "Largest Line", "File"; } { printf "%-15s%s\n", $1, $2; }'

저는 해킹된 사이트에서 해킹된 파일을 찾아 삭제할 수 있도록 이 앱을 사용합니다 -regex.

관련 정보