awk가 너무 느릴 때 필드를 기반으로 대용량 파일을 분할하는 가장 좋은 방법

awk가 너무 느릴 때 필드를 기반으로 대용량 파일을 분할하는 가장 좋은 방법

대용량 .gz 파일(500G 이상)을 처리하는 데 문제가 있습니다. 내 목표는 이 파일의 각 필드를 네 번째 필드로 분리하는 것입니다. 나는 이전에 이 작업을 수행하기 위해 멋진 awk one-liner를 사용했습니다.

zcat file.txt.gz | awk 'NR>1{print >  $4}'

그런데 아쉽게도 대용량 파일을 처리하는 데 시간이 오래 걸리기 때문에 먼저 크기별로 분할한 후 필드별로 분할한 후 각 파일을 연결해 보았습니다. 다음을 사용하여 분할할 수 있습니다.

i=file.txt.gz
dir=$i
mkdir -p $dir
cd $dir
split -b 200M ../$i $i

for file in `ls *`; do zcat $file | awk 'NR>1{print >  $4}'; done

하지만 네 번째 필드를 통해 올바른 파일을 모두 연결하려면 어떻게 해야 합니까? 또한 정말 더 좋은 방법은 없을까요? 또한 "예기치 않은 파일 끝"과 같은 내용의 gz 파일 분할을 사용할 때 오류가 발생하므로 분할도 잘못된 것 같지만 제안이 있는 경우 올바른 방향으로 가고 있는지 잘 모르겠습니다. 도움이 되는.

당신의 도움을 주셔서 대단히 감사합니다! 프라

답변1

Satō Katsura의 파일 설명자 주석은 $4의 다른 값이 1021개 이상(보통 사용자 FD 제한은 1024, stdin/stdout/stderr은 -3) 이상이라고 가정하면 정확합니다.그리고당신은 을 사용하고 있습니다 gawk.

>파일을 사용하거나 인쇄할 때 >>파일은 명시적일 때까지 열려 있으므로 close()스크립트에 FD가 누적됩니다. Gawk v3.0 이전부터 FD 소진( ulimit -n)은 투명하게 처리되었습니다. 열린 파일의 링크된 목록이 탐색되고 LRU(가장 최근에 사용됨)가 "일시적으로" 닫힙니다(FD를 릴리스하기 위해 운영 체제 관점에서 닫힘). gawk내부적으로는 나중에 필요할 경우 투명하게 다시 열 수 있도록 추적됩니다. -W lint호출할 때 추가하면 (v3.1 기준) 이런 일이 발생하는 것을 볼 수 있습니다 .

다음과 같이 문제를 시뮬레이션할 수 있습니다 bash.

printf "%s\n" {0..999}\ 2\ 3\ 0{0..9}{0..9}{0..9} | time gawk -f a.awk

이는 $4의 고유 값 1,000개를 포함하는 1,000,000줄의 출력을 생성하며, 내 노트북에서는 약 17초가 걸립니다. 내 한도는 1024개 FD입니다.

 printf "%s\n" {0..499}\ 2\ 3\ {0..1}{0..9}{0..9}{0..9} | time gawk -f a.awk

이는 또한 1,000,000줄의 출력을 생성하지만 2,000개의 고유 값 $4를 포함하고 약 110초 안에 실행됩니다(6배 이상 더 길고 1백만 개의 추가 작은 페이지 오류 포함).

$4 추적의 관점에서 위의 내용은 출력 파일이 모든 줄을 변경하는 "가장 비관적인" 입력입니다(그리고 필요한 출력 파일이 매번 (다시)열려야 함을 보장합니다).

이 문제를 해결하는 데 도움이 되는 두 가지 방법이 있습니다. 파일 이름 사용의 혼란을 줄이거나(예: $4로 사전 정렬) GNU를 사용하여 입력을 청크하는 것입니다 split.

사전 정렬:

printf "%s\n" {0..499}\ 2\ 3\ {0..1}{0..9}{0..9}{0..9} | 
  sort -k 4 | time gawk -f a.awk

( 필드 번호 sort에 맞게 옵션을 조정해야 할 수도 있습니다 )awk

약 4.0초로 파일 처리가 최소화되므로 첫 번째 경우보다 훨씬 빠릅니다. (대용량 파일을 정렬하면 $TMPDIR또는 의 디스크 임시 파일이 사용될 수 있습니다 /tmp.)

그리고 split:

printf "%s\n" {0..499}\ 2\ 3\ {0..1}{0..9}{0..9}{0..9} | 
  time split -l 1000 --filter "gawk -f a.awk"

이 작업에는 약 38초가 소요됩니다. 따라서 1000개의 프로세스를 시작하는 데 드는 오버헤드가 gawk비효율적인 내부 FD 처리보다 적다는 결론을 내릴 수 있습니다. 이 경우>>대신 사용해야합니다>awk 스크립트에서 그렇지 않으면 각각의 새로운 프로세스가 이전 출력을 파괴합니다. (이를 호출하기 위해 코드의 용도를 변경하면 동일한 경고가 적용됩니다 close().)

물론 다음 두 가지 방법을 결합할 수도 있습니다.

printf "%s\n" {0..499}\ 2\ 3\ {0..1}{0..9}{0..9}{0..9} | 
  time split -l 50000 --filter "sort -k 4 | gawk -f a.awk"

sort제 경우에는 이 작업에 약 4초가 걸렸습니다. 청크(50000)를 조정하면 프로세스/파일 처리 오버헤드와 디스크 사용량 요구 사항을 절충할 수 있습니다 . YMMV.

출력 파일의 수를 미리 알고 있고 너무 크지 않은 경우 루트를 사용하여 늘릴 수 있습니다(예: ulimit -n 8192자신 su). 또는 일반적으로 제한을 조정할 수도 있습니다.모든 프로세스에 대해 열린 파일 제한을 늘리는 방법은 무엇입니까?. 제한은 운영 체제와 해당 구성(운이 좋지 않으면 libc도 가능)에 따라 결정됩니다.

관련 정보