Sed alternativa para pesquisa e substituição em linhas muito longas

9

Eu tenho arquivos que foram gerados por um programa que não colocou novas linhas no final dos registros. Eu quero colocar novas linhas entre os registros, e posso fazer isso com um script sed simples:

sed -e 's/}{/}\n{/g'

O problema é que os arquivos de entrada têm vários gigabytes de tamanho e, portanto, as linhas de entrada para sed têm vários GBs de comprimento. sed tenta manter uma linha na memória, o que não funciona neste caso. Eu tentei a opção --unbuffered , mas isso apenas pareceu torná-la mais lenta e não permitiu que ela fosse concluída corretamente.

    
por Tom Panning 02.03.2015 / 16:26

3 respostas

6

Você pode usar outra ferramenta que permite definir o separador de registro de entrada. Por exemplo

  • Perl

    perl -pe 'BEGIN{ $/="}{" } s/}{/}\n{/g' file
    

    A variável especial $/ é o separador de registro de entrada. Configurá-lo para }{ define linhas como terminando em }{ . Dessa forma, você pode conseguir o que quiser sem ler tudo na memória.

  • mawk ou gawk

    awk -v RS="}{" -vORS= 'NR > 1 {print "}\n{"}; {print}' file 
    

    Esta é a mesma ideia. RS="}{" define o separador de registro como }{ e, em seguida, você imprime } , uma nova linha, { (exceto o primeiro registro) e o registro atual.

por 02.03.2015 / 16:39
3

Perl para o resgate:

perl -i~ -e ' $/ = 24;
              while (<>) {
                  print "\n" if $closing and /^{/;
                  undef $closing;
                  s/}{/}\n{/g;
                  print;
                  $closing = 1 if /}$/;
              } ' input1 input2

A configuração de $/ to 24 lerá o arquivo em blocos de 1024 bytes. A variável $closing manipula o caso quando um pedaço termina em } e o próximo começa com { .

    
por 02.03.2015 / 16:42
2

Você deve fazer:

{ <infile tr \} \n;echo {; } | paste -d'}\n' - /dev/null >outfile

É provavelmente a solução mais eficiente.

Isso coloca um {} para proteger qualquer possível dado final. Com mais um processo tr , você pode trocar isso e fazer uma linha em branco no início do primeiro campo { . Como ...

tr {} '}\n'| paste -d{\0 /dev/null - | tr {}\n \n{}

Então, o primeiro, com os dados de exemplo de don, faz:

printf '{one}{two}{three}{four}' |
{ tr \} \n; echo {; }           |
paste -d'}\n' - /dev/null
{one}
{two}
{three}
{four}
{}

... e o segundo faz ...

printf '{one}{two}{three}{four}'      |
tr {} '}\n'| paste -d{\0 /dev/null - |
tr {}\n \n{}
#leading blank
{one}
{two}
{three}
{four}

Não há nova linha no final do segundo exemplo - embora haja um para o primeiro.

    
por 02.03.2015 / 20:34

Tags