Como posso obter a primeira coluna de um arquivo txt de 300GB?

3

deixe-me primeiro explicar o meu problema em detalhe. Na verdade é bem simples. Eu tenho um enorme arquivo .txt, 300GB para ser mais precioso, e gostaria de colocar todas as strings distintas da primeira coluna, que correspondem ao meu padrão em um arquivo .txt diferente.

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

Isso é o que eu tentei e, até onde eu sei, funciona bem, mas o problema é que depois de algum tempo recebo o seguinte erro:

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

Alguma sugestão para analisar um arquivo deste tamanho?

    
por Jovan Andonov 29.05.2014 / 15:29

3 respostas

1

Parece-me que awk atinge uma linha enorme que resultaria em 32767 ou mais campos. Eu não posso reproduzir isso com o meu awk , embora:

> 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

Você poderia usar uma ferramenta mais robusta contra linhas longas. Você precisa decidir qual é o tamanho máximo do primeiro campo. Se nós assumirmos 100 então você pode tentar isto:

cut -b -100 file | awk ...

Além disso (mas isso não está relacionado ao seu problema), sua construção awk | grep | awk não faz sentido. Isso seria feito assim:

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

sugestão de depuração

Como Ramesh apontou: Pode ser interessante encontrar a linha que está causando o problema. O número da linha do problema deve ser um daqueles que seguem o número impresso (ou gravado em um arquivo) por este comando:

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

Se awk esvaziar seus buffers antes de "travar", então deve ser o próximo número (+1).

    
por 29.05.2014 / 15:52
0

Parece que o seu awk implement tem limite no número de campos.

Exemplo em 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 (define em error.c ) é uma função para gerar uma mensagem de erro em tempo de execução:

/* 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);                                                               
}

E no arquivo 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)

Você pode ver que MAX_FIELD default é 256*128 - 1 = 32767 .

Usar gawk pode resolver esse problema.

    
por 29.05.2014 / 18:29
0
Em geral, quanto mais especializada for uma ferramenta, melhor será lidar com arquivos muito grandes. Você poderia lidar com esse arquivo no awk, lembre-se - você precisaria extrair o primeiro campo manualmente em vez de usar o processamento de campo interno. Você poderia combinar a chamada grep e a segunda chamada awk em uma única chamada awk também.

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

No entanto, um pipeline através de ferramentas especializadas provavelmente será mais rápido. Se os campos sempre usarem uma guia como separador, você poderá usar cut para isolar o primeiro campo. Se o separador for um espaço, faça isso cut -d ' ' .

cut -f 1 | grep … | …

Como alternativa, você pode usar sed para as duas primeiras etapas. Se isso é mais rápido que cut … | grep … depende dos seus dados e da sua implementação. Na chamada sed, substitua \t por um caractere de tabulação literal se sua implementação não entender \t ; se sua implementação não entender \n em uma substituição s , substitua-a por barra invertida-nova linha.

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

Se sempre houver uma única ocorrência de /ns/ no primeiro campo, você poderá simplificar isso para o seguinte, que corresponde à última ocorrência de /ns :

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

Passando para a última etapa, se houver muitas correspondências, o comando awk usará muita memória. Se alterar a ordem das linhas na saída for aceitável, você poderá usar sort -u .

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