300,000개 이상의 행이 있는 FILE_A와 30,000개 이상의 행이 있는 FILE_B가 있습니다. FILE_A의 각 줄을 FILE_B로 grep하고 grep 결과를 새 파일에 쓰는 bash 스크립트를 만들었습니다.
전체 과정은 5시간 이상 소요됩니다.
내 스크립트의 성능을 향상시킬 수 있는 방법이 있다고 생각하시면 어떤 제안이라도 찾고 있습니다.
grep 명령으로 grep -F -m 1 을 사용합니다. FILE_A는 다음과 같습니다:
123456789
123455321
FILE_B는 다음과 같습니다.
123456789,123456789,730025400149993,
123455321,123455321,730025400126097,
따라서 bash에는 FILE_A에서 다음 줄을 선택하고 FILE_B에서 grep하는 while 루프가 있습니다. FILE_B에서 패턴을 찾으면 result.txt에 씁니다.
while read -r line; do
grep -F -m1 $line 30MFile
done < 300KFile
귀하의 도움에 미리 감사드립니다.
답변1
성능의 핵심은 대용량 파일을 한 번만 읽는 것입니다.
여러 패턴을 별도의 줄에 배치하여 grep에 전달할 수 있습니다. 이는 일반적으로 grep에게 파일에서 패턴을 읽도록 지시하여 수행됩니다.
grep -F -f 300KFile 30MFile
이렇게 하면 큰 파일 전체에서 일치 항목을 순서대로 출력하고 여러 패턴과 일치하는 행만 한 번만 인쇄합니다. 또한 이는 라인의 어느 곳에서나 패턴을 찾습니다. 예를 들어 패턴 파일에 포함된 경우 및 1234
와 같은 라인이 일치합니다.123456,345678,2348962342
478912,1211138,1234
전처리 패턴을 통해 정확한 열 일치를 제한할 수 있습니다. 예를 들어, 패턴에 특수 문자가 포함되어 있지 않은 경우 ()?*+\|[]{}
:
<300KFile sed -e 's/^/(^|,)/' -e 's/$/($|,)/' |
grep -E -f - 30MFile
각 패턴의 첫 번째 일치 항목만 유지하는 것이 중요하다면 첫 번째 패스를 만들어 위의 관련 줄만 추출한 다음 awk 또는 Perl에서 두 번째 패스를 만들어 어떤 패턴이 표시되었는지 추적하세요.
<300KFile sed -e 's/^/(^|,)/' -e 's/$/($|,)/' |
grep -E -f - 30MFile |
perl -l -F, -ape '
BEGIN {
open P, "300KFile" or die;
%patterns = map {chomp; $_=>1} <P>;
close P;
}
foreach $c (@F) {
if ($patterns{$c}) {
print;
delete $patterns{$c};
}
}
'
답변2
다음 명령을 실행할 수 있습니까?
grep -Ff FILE_A FILE_B > FILE_C
이제 파일 A와 C에서만 스크립트를 실행할 수 있습니다.
고쳐 쓰다:잠깐... 순서가 유지되나요?
또 다른 업데이트:주문을 유지하려면 추가 처리가 필요합니다. 이렇게 하면 원본 스크립트와 동일한 결과를 얻을 수 있습니다. FILE_A의 300,000개 행과 FILE_B의 300,000개 행에 대해 각각 125분 및 14초 동안 테스트되었습니다.
#! /bin/bash
grep -Ff FILE_A FILE_B > FILE_B_TMP
grep -oFf FILE_A FILE_B_TMP > FILE_A_SHUFF
grep -Ff FILE_A_SHUFF FILE_A > FILE_A_TMP
while read -r line; do
grep -F -m1 "$line" FILE_B_TMP
done < FILE_A_TMP > result.txt
답변3
나는 comm이 더 나은 성능을 낼 수 있다고 믿습니다.
comm -12 300KFile <(sed 's/,.*//' 30MFile)
참고 사항 300KFile의 문자열이 123123
30M 파일의 문자열과 일치해야 하는지 확실하지 않습니다. gdwyedg,123123,hfsjdkfh
귀하의 스크립트에서는 일치하지만 내 스크립트에서는 일치하지 않습니다.
답변4
나는 grep 기반 솔루션이 여전히 FILE_A의 모든 레코드를 FILE_B의 모든 레코드와 비교해야 한다고 생각합니다. FILE_A의 N-1개 이상의 레코드가 FILE_B의 특정 레코드와 일치하지 않기 때문에 이 접근 방식에는 많은 중복이 있습니다. 반면에 파일을 정렬하면 각 비교에서 많은 테스트를 삭제할 수 있습니다. 그래서 뭔가 ...
#!/bin/bash
# NB a faster solution would be to sort the smaller file in a seperate process
# you might also want to set a buffer size for large files
sort $1 > /tmp/$$.a
sort $2 > /tmp/$$.b
join -j1 -t',' /tmp/$$.a /tmp/$$.b
rm -f /tmp/$$.?
(검증되지 않은)
그러나 FILE_B의 특정 데이터 열과 일치시키려는 경우 항목 순서가 변경되고 정렬로 인해 오버헤드가 발생하지만 이러한 파일 크기의 경우 결과가 더 빨라야 합니다.