Extrai variáveis do arquivo de texto em array com Bash, Perl e Regex

0

Eu gostaria de extrair variáveis de um arquivo de texto com Bash, Perl e Regex.

O arquivo tem esta aparência (e já foi lido na variável $ str):

Filename: XXXXX
Type: XXX
Size: XXXX
Unimportant thing: XXXX

Filename: YYYYY
Type: YYY
Size: YYYY
Unimportant thing: YYYY

Eu preciso de nome de arquivo, tipo e tamanho para cada bloco. Uma matriz seria a melhor, mas as strings que contêm essas variáveis separadas por um determinado caractere também são aceitáveis.

No entanto, algumas vezes, alguns dos campos (por exemplo, tamanho ou tipo) estão ausentes. Eu gostaria de omitir esses registros, então eu acho que preciso de um Regex que possa combinar através de várias linhas.

Eu tentei o seguinte:

perl -pe 's/Filename: ([^\n]*)\nType: ([^\n]*)\nSize: ([^\n]*)\n/\t\t\n/' <<< $str

mas isso imprimiu o texto original sem modificações.

Então eu tentei sem o parâmetro p commandline (eu esperava que desse jeito todo o arquivo fosse processado ao invés de iterar as linhas):

perl -e 's/Filename: ([^\n]*)\nType: ([^\n]*)\nSize: ([^\n]*)\n/\t\t\n/' <<< $str

Este não imprimiu nada (resultado vazio).

Então eu tentei adicionar print na frente do Regex porque achei que talvez a remoção de -p fizesse com que o Perl não soubesse que eu queria os resultados impressos:

perl -e 'print s/Filename: ([^\n]*)\nType: ([^\n]*)\nSize: ([^\n]*)\n/\t\t\n/' <<< $str

Ainda não há sucesso (resultado vazio).

O que estou perdendo?

Atualização:

Eu gostaria disso como um comando perl de uma linha.

    
por z32a7ul 24.08.2017 / 22:34

2 respostas

1

Meu conhecimento em Perl é tênue, mas como ninguém mais forneceu uma resposta em Perl, vou tentar.

Transmita seus dados como arquivos e imprima linhas separadas por tabulações com três valores por linha:

perl -e 'while (<>) { $s .= $_; } chomp $s; @arr = split(/\n{2,}/, $s); foreach my $a(@arr) { $a =~ s/Filename: ([^\n]*)\nType: ([^\n]*)\nSize: ([^\n]*)\n.*/$1\t$2\t$3\n/ || next; print "$a"; } ' infile

Resultado:

XXXXX   XXX     XXXX
YYYYY   YYY     YYYY

É um pouco de força bruta, mas funciona dividindo a entrada em parágrafos / blocos e então aplica sua regex multi-linha para cada um.

Detalhes ...

  • while (<>) { $s .= $_; } - Slurp a entrada em uma única string.
  • chomp $s - Remove a nova linha à direita da string.
  • @arr = split(/\n{2,}/, $s) - Dividir cadeia em consecutivos novas linhas. Isso divide em parágrafos / blocos. Armazene os blocos em uma matriz.
  • foreach my $a(@arr) - Faz um loop sobre cada elemento da matriz (bloco). As próximas duas linhas de código são aplicadas a cada bloco.
  • $a =~ s/Filename: ([^\n]*)\nType: ([^\n]*)\nSize: ([^\n]*)\n.*/$1\t$2\t$3\n/ || next - Extrai valores dos três campos de interesse. Se nenhuma substituição ocorrer (o que significa que o regex não corresponde, porque, por exemplo, um valor está faltando), pule este bloco e vá para o próximo.
  • print "$a" - Imprimir o resultado da substituição: os três valores separados por tabulações.

Mais uma vez, eu não uso muito Perl, então provavelmente há soluções mais elegantes do que isso.

    
por 25.08.2017 / 09:22
0

Não é um grande especialista em perl, mas com sed será assim:

sed  -n '/^$/d;/^Filename/,/^Unimportant/{:a;/Unimportant/!{N;ba};s/Filename: \([^\n]*\)\nType: \([^\n]*\)\nSize: \([^\n]*\)\n.*/\t\t/p};'

Onde:

  • /^$/d - removerá todas as linhas vazias
  • /^Filename/,/^Unimportant/ corresponderá a cada bloco do Filename ao Unimportant separadamente. Eu estou supondo que você tenha um registro sem importância em cada bloco.
  • :a;/Unimportant/!{N;ba}; irá concatenar todo o bloco em um buffer. É necessário, pois sed não é capaz de trabalhar com expressões regulares de várias linhas ou processar várias linhas de uma vez de outra maneira
  • s/Filename: \([^\n]*\)\nType: \([^\n]*\)\nSize: \([^\n]*\)\n.*/\t\t/p}; fará a substituição para o formato que você precisa (com base no seu regex perl)
por 24.08.2017 / 23:17