300GB txt 파일의 첫 번째 열을 얻는 방법은 무엇입니까?

300GB txt 파일의 첫 번째 열을 얻는 방법은 무엇입니까?

먼저 내 문제를 자세히 설명하겠습니다. 사실 정말 쉽습니다. 나는 거대한 .txt 파일(정확히 말하면 300GB)을 가지고 있으며 내 패턴과 일치하는 첫 번째 열의 모든 다른 문자열을 다른 .txt 파일에 넣고 싶습니다.

awk '{print $1}' file_name | grep -o '/ns/.*' | awk '!seen[$0]++' > test1.txt

이것이 내가 시도한 것이고 내가 아는 한 잘 작동하지만 문제는 잠시 후 다음과 같은 오류가 발생한다는 것입니다.

awk: program limit exceeded: maximum number of fields size=32767
    FILENAME="file_name" FNR=117897124 NR=117897124

이러한 대용량 파일을 구문 분석하는 데 대한 제안 사항이 있습니까?

답변1

awk나에게는 32767개 이상의 필드가 발생하는 거대한 선 처럼 들립니다 . 그러나 다음으로는 이것을 재현할 수 없습니다 awk.

> echo | awk 'BEGIN {for(i=1;i<100000;i++) printf "%d ",i}; { print ""; }' >file
> awk '{ print $50000; }' too_long_line_for_awk.txt
50000

> awk --version
GNU Awk 4.1.0, API: 1.0

장기적으로 사용할 수 있는 더 강력한 도구가 있습니다. 첫 번째 필드의 최대 길이를 결정해야 합니다. 100이라고 가정하면 다음을 시도해 볼 수 있습니다.

cut -b -100 file | awk ...

또한 (그러나 이것은 귀하의 질문과 관련이 없습니다) 귀하의 awk | grep | awk구성이 의미가 없습니다. 다음과 같이 할 수 있습니다:

awk '$1 ~ "/ns/" {sub("^.*/ns/","/ns/",$1); if( !seen[$1]++ ) print $1}' \
  file_name >test1.txt

디버깅 제안

Ramesh가 지적했듯이 문제를 일으키는 선을 찾는 것이 흥미로울 수 있습니다. 문제 행의 번호는 다음 명령으로 인쇄된(또는 파일에 기록된) 숫자 중 하나여야 합니다.

awk '{ print NR;}' | tail -n 1 >crashline.txt

"충돌" 전에 버퍼를 비운 경우 awk다음 숫자(+1)여야 합니다.

답변2

awk귀하의 도구에는 필드 수에 제한이 있는 것 같습니다 .

mawk:

field.c:

/*------- more than 1 fbank needed  ------------*/                              

/*                                                                              
  compute the address of a field with index                                     
  > MAX_SPLIT                                                                   
*/                                                                              

CELL *                                                                          
slow_field_ptr(int i)                                                           
{                                                                               
    ....                                                                   
    if (i > MAX_FIELD)                                                          
        rt_overflow("maximum number of fields", MAX_FIELD);
    ....
}

rt_overflow(에 정의됨 error.c)은 런타임에 오류 메시지를 생성하는 함수입니다.

/* run time */                                                                  
void                                                                            
rt_overflow(const char *s, unsigned size)                                       
{                                                                               
    errmsg(0, "program limit exceeded: %s size=%u", s, size);                   
    rt_where();                                                                 
    mawk_exit(2);                                                               
}

그리고 파일에서 size.h:

#define  FBANK_SZ    256                                                        
#define  FB_SHIFT      8    /* lg(FBANK_SZ) */                                  
#else                                                                           
#define  FBANK_SZ   1024                                                        
#define  FB_SHIFT     10    /* lg(FBANK_SZ) */                                  
#endif                                                                          
#define  NUM_FBANK   128    /* see MAX_FIELD below */                           

#define  MAX_SPLIT  (FBANK_SZ-1)    /* needs to be divisble by 3 */             
#define  MAX_FIELD  (NUM_FBANK*FBANK_SZ - 1)

보시다시피 MAX_FIELD기본값은 입니다 256*128 - 1 = 32767.

사용하면 gawk이 문제를 해결할 수 있습니다.

답변3

일반적으로 도구가 전문화될수록 매우 큰 파일을 더 잘 처리할 수 있습니다. 이 파일은 awk에서 처리할 수 있습니다. 내장된 필드 처리를 사용하는 대신 첫 번째 필드만 수동으로 추출하면 됩니다. grep 호출과 두 번째 awk 호출을 하나의 awk 호출로 결합할 수도 있습니다.

awk -F '\n' '
    { sub(/[\t ].*/,"");
      if (match($0, "/ns/")) $0 = substr($0,RSTART); else next; }
    !seen[$0]++
'

그러나 특수 도구를 사용하여 배관하는 것이 더 빠를 수 있습니다. 필드가 항상 탭 문자를 구분 기호로 사용하는 경우 를 사용 cut하여 첫 번째 필드를 분리할 수 있습니다. 구분 기호가 공백인 경우 로 설정합니다 cut -d ' '.

cut -f 1 | grep … | …

또는 sed를 사용하여 처음 두 단계를 수행할 수 있습니다. 이것이 더 빠른지 여부는 cut … | grep …데이터와 구현에 따라 다릅니다. sed 호출에서 \t구현이 이해하지 못하는 경우 리터럴 탭으로 바꾸고, \t구현이 이해하지 못하는 경우 백슬래시 줄 바꿈으로 \n바꾸세요 .s

sed -n -e 's/[ \t].*//' \
    -e 's!/ns/!\n&!' -e 'b' \
    -e 's/^.*\n//p'

/ns/첫 번째 필드가 항상 한 번 발생하는 경우 마지막 발생과 일치하는 다음으로 단순화할 수 있습니다 /ns.

sed -n -e 's/[ \t].*//' -e 's!.*/ns/!/ns/!p'

마지막 단계로 이동하여 일치하는 항목이 많으면 awk 명령은 많은 메모리를 사용합니다. 출력에서 줄 순서를 변경하는 것이 허용되는 경우 이를 sort -u대신 사용할 수 있습니다.

cut -f 1 | grep -o '/ns/.*' | sort -u
sed -n -e 's/[ \t].*//' -e 's!.*/ns/!/ns/!p' | sort -u

관련 정보