줄 순서를 유지하면서 중복 줄 제거


14
[root@server]# awk '!seen[$0]++' out.txt > cleaned
awk: (FILENAME=out.txt FNR=8547098) fatal error: internal error
Aborted
[root@server]#

""서버 "는 8GB RAM + 16GB SWAP, x> 300GB 여유 공간, amd64, 데스크탑 CPU입니다. 과학적 리눅스 6.6. LOAD를 만들기 위해 실행되는 다른 것은 없습니다. 몇 초 후에 Awk가 중단됩니다. out.txt는 ~ 1.6GB입니다. GNU Awk 3.1.7.

질문 : 줄 순서를 유지하면서 중복 줄을 어떻게 제거합니까? 예도 중요합니다. 예 : "A"와 "a"는 서로 다른 두 줄입니다. 그러나 "a"와 "a"는 중복되며 첫 번째 것만 필요합니다.

대답은 무엇이든 될 수 있습니다. awk가 이것에 좋지 않다면 .. 그러면 perl / sed .. 무엇이 문제 일 수 있습니까?

[root@server]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 61945
max locked memory       (kbytes, -l) 99999999
max memory size         (kbytes, -m) unlimited
open files                      (-n) 999999
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 99999999
cpu time               (seconds, -t) unlimited
max user processes              (-u) 61945
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
[root@server]# 

업데이트 : RHEL 컴퓨터 에서이 작업을 시도했지만 중단되지 않지만 완료 될 때까지 시간이 없었습니다. SL Linux가 RHEL과 다른 이유는 무엇입니까?

업데이트 : 우분투 14 가상 게임을 시도하고 있습니다. 지금까지는 작동합니다! 그것은 ulimit 문제가 아닙니다 : mawk 1.3.3

root@asdf-VirtualBox:~# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 51331
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 51331
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
root@asdf-VirtualBox:~# 

2
귀하의 예에 중복 줄이 없습니다 ...?
mikeserv

1
awk두 머신의 버전은 무엇입니까 ?
cuonglm

최신 rhel 및 최신 sl 리눅스는 rhel 버전을 모른다. sl은 : GNU Awk 3.1.7
somelooser28533

얼마나 큽 out.txt니까? 작은 파일에서 시도하면 같은 명령이 작동합니까? 컴퓨터에 몇 명의 사용자가 있습니까? 프로세스에 사용 가능한 메모리가 충분 했습니까? 입력 파일의 8547098 행에 특별한 것이 있습니까?
terdon

답변:


22

나는 그것이 차이를 만들지 의심하지만, 만약을 대비하여 Perl에서 똑같은 일을하는 방법이 있습니다 :

perl -ne 'print if ++$k{$_}==1' out.txt

문제가 메모리에 고유 한 줄을 유지하는 경우 awk시도한 것과 같은 문제가 있습니다. 따라서 다른 접근 방식은 다음과 같습니다.

cat -n out.txt | sort -k2 -k1n  | uniq -f1 | sort -nk1,1 | cut -f2-

작동 방식 :

  1. GNU 시스템에서, cat -n줄 번호는 각 줄 앞에 약간의 공백을두고 <tab> 문자를 붙입니다. cat이 입력 표현을로 파이프합니다 sort.

  2. sort-k2옵션은 단지 라인이 끝날 때까지 정렬 때 두 번째 필드에서 문자를 고려하도록 지시 sort분할 공백에 기본적으로 필드 (또는 cat의 삽입 공간을하고 '를 <탭> ) .
    다음에 할 때 -k1n, sort먼저 2 필드를 고려하고 둘째-에서 동일의 경우 -k2필드-는 1 필드를 고려하지만, 수치 적으로 분류한다. 따라서 반복되는 줄은 함께 정렬되지만 순서대로 나타납니다.

  3. 결과는 uniq첫 번째 필드 ( -f1및 공백으로 구분됨) 를 무시하도록 파이프되고 원본 파일에 고유 한 행 목록이 생성되어 다시 파이프됩니다 sort.
  4. 이 시간 sort 는 첫 번째 필드 ( cat삽입 된 줄 번호)를 숫자로 정렬하여 정렬 순서를 원래 파일에 있던 순서로 다시 가져 와서이 결과를로 파이프합니다 cut.
  5. 마지막으로 cut에 의해 삽입 된 줄 번호를 제거합니다 cat. 이는 cut두 번째 필드에서 줄 끝까지 만 인쇄하여 적용됩니다 (및cut 기본 구분 기호는 <tab> 문자 임) .

설명하기 위해 :

$ cat file
bb
aa
bb
dd
cc
dd
aa
bb
cc
$ cat -n file | sort -k2 | uniq -f1 | sort -k1 | cut -f2-
bb
aa    
dd
cc

안녕 Terdon, OP는 라인의 순서를 유지해야하므로 cat | sort | uniq 방법이 작동하지 않습니다 ... 비록 당신의 펄 버전처럼 ...
Lambert

1
좋은 해결책 sort! 그러나 대부분이 sort할 수있는 uniq자체가 그래서 당신은 짧은 할 수 있습니다 스크립트 sort -uk2 | sort -bk1,1n
타스

@Costas가 가장 sort?니까? 나는 -uGNU 기능 이라고 생각했다 .
terdon

@ don_crissti 아, 감사합니다. 그래도 어떻게 사용할 수 있습니까? 방금 알았고 수정하기 위해 편집 한 것처럼 줄 순서를 유지하려면 두 번째 필드를 먼저 정렬 한 다음 첫 번째 숫자를 정렬해야합니다. 그런 다음 -u첫 번째 필드를 무시 하도록 어떻게 사용 하고 지정할 수 있습니까? 에 따르면 man sort의이 -u사용할 수있는 옵션이없는 -f내가 여기에 사용할 수 있습니다 생각하지 않도록,.
terdon

1
이것은 Schwartzian 변환입니다 ! (+1)
JJoao

7
#!/usr/bin/perl 
use DB_File;
tie %h, 'DB_File';

while(<>){ not $h{$_} and print and $h{$_}=1 }

편집 1 : 실제로 작동합니까? (비교)

Sol1 : Terdon et all Schwartzian-transform-like one-liner
    cat -n _1 | sort -uk2 | sort -nk1 | cut -f2-

Sol2 : perl  + DB_File (this answer)
    perl dbfile-uniq _1

Sol3 : PO (John W. Gill solution has a similar behavior)
    awk '!seen[$0]++' _1

Sol4: Terdon perl
    perl -ne 'print if ++$k{$_}==1' _1

Case1 : 100_000_000 난수 (각 5 자리), 566Mbytes, 31_212 개의 다른 값 :

$ while true ; do echo $RANDOM; done | head -100000000 > _1

사례 2 : 50_000_000 랜드 번호 (각 10 자리), 516MB, 48_351_464의 다른 값 :

$ shuf _1 |  sed 'N;s/\n/ /' > _11

(다음 숫자는 정확하지 않습니다) :

┌────────┬────────┬────────────────┬────────┬──────┐
         Sol1    Sol2            Sol3    Sol4 
         sort...│ perl DB         awk     perl 
├────────┼────────┼────────────────┼────────┼──────┤
 case 1  6m15    6m17            0m28    0m28 
├────────┼────────┼────────────────┼────────┴──────┤
 case 2  11m15   81m44           out of memory 
├────────┼────────┼────────────────┼────────┬──────┤
 case 2          5m54 /cache=2G               
└────────┴────────┴────────────────┴────────┴──────┘

캐시가있는 sol2는 다음과 같습니다.

use DB_File;
use Fcntl ;

$DB_HASH->{'cachesize'} = 2000_000_000;
tie %h, 'DB_File', "_my.db", O_RDWR|O_CREAT|O_TRUNC, 0640, $DB_HASH;

while(<>){ not $h{$_} and print and $h{$_}=1 }

캐시 크기 옵션을 추가하여 정렬을 최적화 할 수도 있습니다 (완료되지 않음).

하나의 빠른 결론 :

  • sort 환상적인 명령입니다!

1
sort -uk2그리고 sort -nk1,1다릅니다. 첫 번째는 2cd 키부터 줄 끝까지를 고려하고 두 번째 는 첫 번째 키만 고려 합니다 . 당신은 sort -nk1거기에서 변경해야 합니다-그것은 더 빠를 수도 있지만 확실히 더 신뢰할 것입니다. 그건 그렇고-그것들은 예쁜 상자입니다.
mikeserv

@ mikeserv, 의견 주셔서 감사합니다. K1,1은 고유하므로 sort -nk1 및 sort -nk1,1은 일부 결과를 반환합니다. 나는 두 가지를 모두 시도했는데 결과는 같았고 시간은 독특하지 않았다.
JJoao

시도해 주셔서 감사합니다. 그래서 cat -n않습니다 탭을 ? 그 명령이 어떻게 작동하는지 모르겠습니다.
mikeserv

1
@mikeserv, 행복하게 cat -n각 언어 line로 변환 spaces + the number + \t + line-정렬 및 잘라내
기에

1

나는 사용했다

awk -v BINMODE=rw '!($0 in a){a[$0];print}' infile >> outfile

BINMODE = rw : 줄 끝 종결자를 만족시킵니다. (혼합 OS 환경에 살고 있습니다)

논리는 간단합니다.

현재 행이 연관 배열에 없으면 연관 배열에 추가하고 출력으로 인쇄하십시오.

이 방법에는 메모리 제한이있을 수 있습니다. 매우 큰 파일과 파일 세트의 경우 파일 저장소를 사용하여 한계를 극복하기 위해 변형을 사용했습니다.


0

문제의 순서 유지 의미론에는 놀라운 속성이 있습니다. 문제를 세분화 할 수 있습니다. split -l 1000000입력 파일에서 수행 할 수 있습니다 . 그것이 생산하는 1000000 라인 조각은 어휘 적으로 정렬 된 이름을 가지고 있습니다. 그런 다음 조각을 uniqify; 그런 다음 (두 번째 단계로) 출력을 uniqify하십시오.

이는 멀티 패스 솔루션으로 전환하는 대신 메모리 부족 문제 (메모리 요구량 상한)를 해결합니다.

구체적으로 특별히:

입력 데이터를 생성하십시오.

$ cat make-uniqm-input.py
#!/usr/bin/env python
import random
n = 1000000
for i in xrange(0, n):
    print random.randint(1000, 2000)

$ python make-uniqm-input.py  > uniqm-input.txt

$ wc -l uniqm-input.txt
 1000000 uniqm-input.txt

입력 데이터를 분할하십시오.

$ split -l 10000 uniqm-input.txt

$ ls x?? | head
xaa
xab
xac
xad
xae
xaf
xag
xah
xai
xaj

$ ls x?? | wc -l
     100

$ cat x?? | wc -l
 1000000

uniqifier를 한 번에 모두 실행하십시오 (메모리에 모든 고유 한 입력 라인을 유지).

# 'uniqm' is any order-preserving uniq implementation, such as
# gawk '!counts[$0]++'.
$ uniqm < uniqm-input.txt > output-no-splitting.txt

$ wc -l output-no-splitting.txt
    1001 output-no-splitting.txt

분할 조각에서 uniqifier를 실행 한 다음 (메모리의 각 조각에서 고유 한 입력 라인 만 유지) 두 번째 패스로 줄이십시오.

$ for x in x??; do uniqm < $x; done | uniqm > output-with-splitting.txt

$ wc -l output-with-splitting.txt
    1001 output-with-splitting.txt

비교:

$ diff output-no-splitting.txt output-with-splitting.txt

$ head uniqm-input.txt
1506
1054
1623
1002
1173
1400
1226
1340
1824
1091

$ head output-with-splitting.txt
1506
1054
1623
1002
1173
1400
1226
1340
1824
1091

입력에서 고유하지 않은 라인과 고유하지 않은 라인의 비율 또는 입력 라인의 혼합 비율을 알지 못하므로 필요한 분할 파일 수와 관련하여 약간의 조정이 있습니다.


0

다른 접근법 (별도의 답변으로 게시해야 함)은 임시 파일을 생성하는 분할 파일 방식 대신 uniqifier 소프트웨어 자체에서 일괄 처리를 수행합니다. 예를 들어, 설명을 위해 Ruby uniqifier 구현을 사용하는 경우 :

require 'set'
line_batch_count = 50000 # tunable parameter
lines_seen = Set.new
line_number = 0
ARGF.each do |line|
   line_number += 1
   if (line_number % line_batch_count) == 0
     lines_seen.clear
   end
   unless lines_seen.include? line
      puts line
      lines_seen << line
   end
end

아이디어는 항상 해시 세트를 지우는 것입니다. 그런 다음 반복됩니다.

$ cat uniqm-input.txt | ruby uniqm-capped.rb | wc -l
   20021

$ cat uniqm-input.txt | ruby uniqm-capped.rb | ruby uniqm-capped.rb | wc -l
    1001

$ cat uniqm-input.txt | ruby uniqm-capped.rb | ruby uniqm-capped.rb | head
1506
1054
1623
1002
1173
1400
1226
1340
1824
1091

따라서 줄 수가 한 반복에서 다음 반복으로 변경되지 않을 때 까지이 제한 버전을 반복적으로 실행할 수 있습니다.

이 capped-uniqm 기술은 언어에 독립적입니다. lines_seenawk, python, perl, C ++ 등을 사용하는지 여부에 관계없이 N 줄마다 배열을 지울 수 있습니다. 이러한 모든 언어에 대해 명확한 방법이 있습니다. 나는 생각 awk들 ' delete비표준하지만 일반적입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.