![여러 파일에서 zgrep 성능이 느려짐](https://linux55.com/image/174032/%EC%97%AC%EB%9F%AC%20%ED%8C%8C%EC%9D%BC%EC%97%90%EC%84%9C%20zgrep%20%EC%84%B1%EB%8A%A5%EC%9D%B4%20%EB%8A%90%EB%A0%A4%EC%A7%90.png)
나는9.8GBgzip 파일 A.gz와 내가 가지고 있는 다른 파일은 다음과 같습니다.79MBB.txt에는 각 줄에 일부 텍스트가 있습니다. A.gz에서 B의 텍스트를 grep하여 새 파일에 쓰고 싶습니다.
처음에는 이 명령을 사용했습니다.
zgrep -f B.txt A.gz > C.xml
하지만 이 명령은 중단되고 오랫동안 빈 C.xml을 생성합니다.
그런 다음 인터넷 검색 후 B.txt가 크기 때문에 텍스트를 버퍼에 유지하는 동안 중단된다는 것을 알게 되었습니다.
그래서 텍스트 파일을 각각 20000개의 텍스트로 분할했습니다.
split -l 20000 -a 4 B.txt B
Baaaa, Baaab... 같은 파일을 만들었습니다.
그런 다음 각 파일을 반복합니다.
cd B
for f in B*; do
zgrep -f "$f" ../A.gz >> C.xml
done
매우 느리고 여전히 실행 중입니다.
이를 수행하는 더 좋은 방법이 있습니까?
gz 파일을 압축하면 성능이 향상됩니까?
고쳐 쓰다
-F를 사용해 보았습니다.
zgrep -F -f "$f" ../A.gz >> C.xml
조금 더 빠르지만 여전히 다른 옵션이 필요합니다.
나는 이런 XML을 가지고있다
<root>
<source>source1</source>
<Id>123</Id>
<category>ABC</category>
</root>
<root>
<source>source2</source>
<Id>123</Id>
<category>XYZ</category>
</root>
여기서 ID는 123과 동일하지만 카테고리는 ABC와 XYZ가 다릅니다.
(입력은 ABC, DEF, GHI, JKLM, NOP와 같은 제한된 범주 집합입니다.) 처음에 내 범주는 ABC이므로 범주 ABC를 기반으로 해당 ID가 123인 것을 찾아서 속한 모든 ID를 계속 작성합니다. 카테고리는 아래와 같이 새 파일, 즉 B.txt(ID 목록)에 입력됩니다.
zgrep -E 'ABC|DEF|GHI|JKLM|NOP' A.gz | sed -n 's:.*<Id>\(.*\)</Id>.*:\1:p' | uniq > B.txt
나중에 이 ID를 반복하고 모든 xml을 가져와 ID 123의 카테고리 ABC 및 XYZ에 속하는 xml 태그를 얻습니다.
답변1
79MB grep "string"은 작업하기가 어려울 것입니다. 이 줄은 B.txt
정규 표현식인가요, 아니면 고정된 동일한 문자열인가요? 고정 문자열인 경우 A.gz
전체 줄에서 동일하게 표시됩니까? Uncompressed의 행 수는 A.gz
의 행과 일치할 것으로 예상됩니까 B.txt
?
패턴 일치 제안
의 줄이 B.txt
실제로 정규식이거나 줄의 하위 문자열 인 경우 A.gz
다음과 같은 것을 사용해야 할 수도 있습니다.하이퍼스캔거대한 정규식을 처리하도록 설계되었습니다. 디스크 공간이 충분하다면 압축을 풀고 A.gz
HyperScan이 작동하도록 할 수 있습니다(HyperScan이 검색하는 동안 쉘이 즉시 압축을 풀도록 할 수도 있습니다). 시도해 볼 수 있는 또 다른 대안은 다음과 같습니다.립그렙.
전체 라인 매칭 제안
고정된 전체 줄 문자열을 처리 B.txt
하고 압축되지 않은 문자열에 A.gz
상대적으로 작은(예: 100MB 정도) 일치하는 줄이 포함되어 있는 경우 전처리 프로그램을 작성하는 것이 더 나을 수 있습니다 A.gz
.
- 각 행을 해시
B.txt
하고 해시를 기억할 수 있습니다. - 그런 다음 압축되지 않은 해시의 행이
A.gz
이전 해시와 동일한지 확인합니다. 그렇다면C.txt
추가 처리를 준비하기 위해 해당 행을 인쇄합니다(예: Enter ). - 이제 마지막 검사를 수행합니다. 각 줄이
B.txt
그 안에 있는지 더 엄격하게 검사합니다C.txt
(또는 그 반대 - 어떤 파일이 더 작은지에 따라 다름).
초기 근사 필터링을 수행하는 일부 코드는 다음과 같습니다.
# Do a quick APPROXIMATE filter of lines in FILENEEDLES that are also in
# FILEHAYSTACK
import sys
def main():
if len(sys.argv) < 2:
print("usage: %s FILENEEDLES FILEHAYSTACK" % sys.argv[0])
exit(1)
first_filename = sys.argv[1]
second_filename = sys.argv[2]
line_hashes = set()
with open(first_filename, "r") as f:
for line in f:
line_hashes.add(hash(line))
with open(second_filename, "r") as f:
for line in f:
if hash(line) in line_hashes:
sys.stdout.write(line)
if __name__ == "__main__":
main()
예를 들어:
$ echo -e '1\n2\n3' > B.txt
$ echo -e '2\n3\n4\5' | gzip > A.gz
$ ./approxfilter.py B.txt <(gzip -dc A.gz) > candidates.txt
$ cat candidates.txt
2
3
이제 행 출력이 정확히 일치하는지 확인하기 위해 Candidate.txt를 확인해야 합니다 B.txt
(그러나 이는 더 작고 쉬운 문제이길 바랍니다. 후보 행 수가 "작은" 경우 위 프로그램을 수정하여 모든 작업을 수행할 수도 있습니다." 메모리에 보관할 수 있는 범위 내에서) (질문자는 나중에 주석에서 전체 줄 길이 문자열을 사용하지 않으므로 이 접근 방식이 작동하지 않는다는 점을 명확히 했습니다).
답변2
두 번째 시도는 압축 해제를 통해 개선될 가능성이 높습니다. 그렇지 않으면 루프를 반복할 때마다 전체 압축 해제 오버헤드가 발생합니다. 미리 압축을 해제하면 해당 오버헤드가 한 번만 발생한다는 의미입니다.
그래도 속도가 충분히 빠르지 않다면 멀티스레딩을 시도해 볼 수도 있습니다(A가 압축 해제되었다고 가정).
find B -type f -name 'B*' -print0 \
| xargs -0 -t -n1 -P8 \
grep -f {} A >> C.xml
이 예에서는 8개의 프로세스를 동시에 실행해야 하며, 보유한 프로세서/코어 수에 따라 이 값을 조정해야 할 수도 있습니다.
예상되는 속도 결과가 무엇인지 잘 모르겠습니다. 솔직히 말해서 많은 작업을 수행하고 많은 시간이 걸리는 것처럼 들립니다.
답변3
zgrep
그냥 쉘 스크립트 래퍼입니다 grep
. 단순히 grep
시스템에 설치된 모든 것을 실행 하고 이를 사용하여 gzip
입력 파일의 압축을 풉니다.
GNU grep 버전 3.5 또는 3.6을 사용하는 경우 패턴 파일 처리 속도를 저하시켜 설명하는 대로 극심한 성능 저하를 일으키는 버그가 최근 발견되었습니다.
grep 3.7 릴리스 노트에 있는 버그가 있는 예제 패턴 파일은 약 48Mb의 패턴이므로 내가 아는 한 크기는 문제가 되지 않습니다.