Em Perl:
#!/usr/bin/env perl
use strict;
use warnings;
use re qw(eval);
my %field_widths = (
A1 => 10,
B1 => 4,
C1 => 7,
#...(fill this up with the widths of your 38 record types)
);
# Make a regex of record types; sort with longest first as appropriate for
# ... regex alternation:
my $record_type_regex = join '|', sort { length($b) <=> length($a) } keys %field_widths;
my %records;
my $marker_length=7; #Assuming the marker is 7 characters long
while(<>){
chomp;
while( # Parse each line of input
m!
(.{$marker_length}) # Match the record marker (save in $1)
($record_type_regex) # Match any record type (save in $2)
(
(??{'.'x$field_widths{$2})} # Match a field of correct width
) # Save in $3
!xg){
$records{$2}.="$1$2$3\n";
}
}
for my $file (sort keys %records){
open my $OUT,'>',$file or die "Failed to open $file for writing: $!\n";
print $OUT $records{$file};
close $OUT
}
Invoque como:
[user@host]$ ./myscript.pl file_of_data
Código testado e funciona com sua entrada específica.
Atualizar
Nos seus comentários, você solicitou um "equivalente Unix" dos itens acima. Eu duvido que exista tal coisa, uma vez que a expressão Perl usada para analisar sua linha é uma expressão altamente irregular e duvido que expressões regulares de baunilha possam analisar seu formato de dados: é muito semelhante a um tipo famoso de expressão que regex pode 't analisar (corresponde a qualquer número de a
seguido pelo mesmo número de b
).
Em qualquer caso, a abordagem "Unix" mais próxima que eu posso encontrar é a generalização da resposta do 1_CR . Você deve observar que essa abordagem é específica para a implementação GNU de grep
e, portanto, não funcionará na maioria dos Unices. A abordagem do Perl, ao contrário, deve funcionar da mesma forma em qualquer plataforma em que o Perl trabalhe. Aqui está a minha abordagem sugerida do GNU grep
:
cat <<EOF \
| while read -r record width;do
grep -oE ".{7}$record.{$width}" input_file\ #replace 7 with marker length
>> "$record"
done
A1 10
B1 4
# enter your 38 record types
EOF
Atualizar
Com base nas solicitações do OP nos comentários, em vez de passar o nome do arquivo como um argumento de linha de comando, ele pode ser aberto no script da seguinte forma:
open my $IN,'<',$input_file_name or die "Failed to open $input_file: $!\n";
while(<$IN>){ #instead of while(<>)
...
Isso pressupõe que você tenha declarado a variável $input_file_name
para conter, bem, o nome do arquivo de entrada.
Como para acrescentar um timestamp ao nome do arquivo de saída, você pode usar a sintaxe qx{}
: entre as chaves, você pode colocar qualquer comando Unix que desejar e ele será executado e sua saída padrão será lida no lugar do% operadorqx{}
:
open my $OUT,'>',"$file_".qx{date +%Y-%m-%d--%I:%M:%S%P}
O operador qx
não está restrito a chaves, use seu caractere favorito como delimitador, apenas certifique-se de que ele não esteja no comando que você precisa executar:
qx<...>
qx(...)
qx!...!
qx@...@
e assim por diante ...
Em algum código Perl você pode ver backticks ( ' '
) usados para servir esta função, similar ao que o shell faz. Basta pensar no operador qx
como a generalização de backticks para qualquer delimitador.
Aliás, isso dará um timestamp ligeiramente diferente para cada arquivo (se a diferença de seus tempos de criação for um número finito de segundos). Se você não quiser isso, você pode fazer isso em duas etapas:
my $tstamp = qx{...};
open my $OUT,'>',"$file_$tstamp" or die...;