divide o arquivo em duas partes, em um padrão

12

Como dividir um arquivo grande em duas partes, em um padrão?

Dado um exemplo file.txt :

ABC
EFG
XYZ
HIJ
KNL

Desejo dividir este arquivo em XYZ , de modo que file1 contenha linhas até XYZ e restante das linhas em file2 .

    
por d.putto 10.05.2015 / 12:36

6 respostas

9

Com awk , você pode fazer:

awk '{print >out}; /XYZ/{out="file2"}' out=file1 largefile


Explicação: O primeiro argumento awk ( out=file1 ) define uma variável com o nome do arquivo que será usado para saída enquanto o argumento subseqüente ( largefile ) é processado. O programa awk imprimirá todas as linhas no arquivo especificado pela variável out ( {print >out} ). Se o padrão XYZ for encontrado, a variável de saída será redefinida para apontar para o novo arquivo ( {out="file2}" ) que será usado como destino para imprimir as linhas de dados subseqüentes.

Referências:

  • manual do gawk: Redirecionamento link
por 10.05.2015 / 13:01
12

Este é um trabalho para csplit :

csplit -sf file -n 1 large_file /XYZ/

dividiria o arquivo em% {co_de%}, criando peças com pré s ix f e file umbered usando um único dígito, por exemplo n etc. Observe que usar file0 seria dividido, mas não incluindo a linha que corresponde a /regex/ . Para dividir em e incluindo a linha correspondente regex , adicione um regex offset:

csplit -sf file -n 1 large_file /XYZ/+1

Isso cria dois arquivos, +1 e file0 . Se você realmente precisar que eles sejam denominados file1 e file1 , você sempre poderá adicionar um padrão vazio ao comando file2 e remover o primeiro arquivo:

csplit -sf file -n 1 large_file // /XYZ/+1

cria csplit , file0 e file1 , mas file2 está vazio, para que você possa removê-lo com segurança:

rm -f file0
    
por 10.05.2015 / 21:04
6

Com um ksh moderno, aqui está uma variante shell (ou seja, sem sed ) de uma das respostas baseadas em sed acima:

{ read in <##XYZ ; print "$in" ; cat >file2 ;} <largefile >file1


E outra variante em ksh sozinho (ou seja, omitindo também o cat ):

{ read in <##XYZ ; print "$in" ; { read <##"" ;} >file2 ;} <largefile >file1


(A solução ksh pura parece ser bastante eficiente; em um arquivo de teste de 2,4 GB ela precisou de 19 a 21 segundos, em comparação com 39 a 47 segundos com a abordagem baseada em sed / cat ).

    
por 10.05.2015 / 15:31
6
{ sed '/XYZ/q' >file1; cat >file2; } <infile

Com o GNU sed , você deve usar a opção -u nbuffered. A maioria dos outros sed s deve funcionar apenas.

Para sair do XYZ ...

{ sed -n '/XYZ/q;p'; cat >file2; } <infile >file1
    
por 10.05.2015 / 13:47
3

Tente isso com o GNU sed:

sed -n -e '1,/XYZ/w file1' -e '/XYZ/,${/XYZ/d;w file2' -e '}' large_file
    
por 10.05.2015 / 12:53
1

Um truque fácil é imprimir em STDOUT ou STDERR, dependendo se o padrão de destino foi correspondido. Você pode então usar os operadores de redirecionamento do shell para redirecionar a saída de acordo. Por exemplo, em Perl, supondo que o arquivo de entrada seja chamado de f e os dois arquivos de saída f1 e f2 :

  1. Descartar a linha que corresponde ao padrão de divisão:

    perl -ne 'if(/XYZ/){$a=1; next} ; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
    
  2. Incluindo a linha correspondente:

    perl -ne '$a=1 if /XYZ/; $a==1 ? print STDERR : print STDOUT;' f >f1 2>f2
    

Como alternativa, imprima em diferentes identificadores de arquivo:

  1. Descartar a linha que corresponde ao padrão de divisão:

    perl -ne 'BEGIN{open($fh1,">","f1");open($fh2,">","f2");}
    if(/XYZ/){$a=1; next}$a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
    
  2. Incluindo a linha correspondente:

    perl -ne 'BEGIN{open($fh1,">","f1"); open($fh2,">","f2");}
              $a=1 if /XYZ/; $a==1 ? print $fh1 "$_" : print $fh2 "$_";' f
    
por 10.05.2015 / 21:13