Ajuda com shell script awk / sed

1

Eu tenho que fazer um script usando as informações na tabela a seguir (informações falsas)

AnimalNumber,DOB,Gender,Breed,Date-moved-in
IE161289240602,04/02/2010,M,AAX,20/07/2011,
IE141424490333,13/01/2009,M,LMX,21/09/2010,
IE151424420395,19/01/2007,F,LMX,20/08/2010,

basicamente eu preciso listar apenas o DOB e animalnumber , mas o número do animal deve ser quebrado assim

IE161289240602 deve ser 1612892 4 0602

e também apenas o mês e o ano de nascimento devem ser listados para algo assim na primeira linha

Feb 2010 1412892 4 0602

Alguma idéia de como fazer isso? Receio que seja um pouco fora do meu conjunto de habilidades

    
por johndoe12345 05.10.2015 / 10:38

3 respostas

2

Para o GNU awk

awk -F, '
    NR>1{
        sub("..","")                   #remove first two letters (mean IE)
        d=""
        for(i=split($2,D,"/");i>0;i--) #format 2nd field into 'YY MM DD' 
            d=d D[i] " "
        print strftime("%b %Y",mktime(d 0" "0" "0)),gensub("[0-9]"," & ",8,$1)
    }' file
  • mktime produz timestamp em segundos de EPOCH da string no formato YYYY MM DD HH MM SS
  • strftime converte o registro de data e hora no formato desejado (no caso %b %Y )
  • gensub substitui 8 dígito ( [0-9] ) no primeiro campo ( $1 ) sozinho ( & ) com espaços à direita

Nós vemos apenas a formatação de strings para usar sed :

sed -r '
    1d
    s/./ & /10
    s|(../)(../)||
    s/..([^,]*),([^,]*).*/date -d "" +"%b %Y "/e
    ' file

ou para sed sem o comando e

sed '
    1d
    s/./ & /10
    s|\(../\)\(../\)||
    s/..\([^,]*\),\([^,]*\).*/date -d "" +"%b %Y "/
    ' file | bash

ou

sed '
    s/./ & /10
    s/../+"%b %Y /
    s/,/" -d /
    s|\(../\)\(../\)||
    s/,/\n/
    1!P
    d' file | xargs -n3 date
    
por 05.10.2015 / 11:24
2

Eu estaria pensando em "usar perl":

#!/usr/bin/env perl 
use strict;
use warnings;

use Time::Piece;

#get the column names out of the file. We remove the trailing linefeed. 
#<> is the magic input file handle, so it reads from STDIN or files
#specified on command line, e.g. myscript.pl file_to_process.csv
my @headers = split ( /,/, <> =~ s/\n//r );

while ( <> ) { 
    chomp; #strip linefeed. 
    my %stuff;
    #this makes use of the fact we know the headers already
    #so we can map from the line into named columns. 
    @stuff{@headers} = split /,/; #read comma sep into hash

    #DOB:
    #take date, parse it into a unix time, then use strftime to output "Mon year"
    print Time::Piece -> strptime ( $stuff{'DOB'}, "%d/%m/%Y" ) -> strftime("%b %Y");
    #regex match against AnimalNumber, and then join it with space separation. 
    print "\t"; #separator
    print join ( " ", $stuff{'AnimalNumber'} =~ m/(\d+)(\d)(\d{4})/ );
    print "\n";
}

Esta saída:

Feb 2010    1612892 4 0602
Jan 2009    1414244 9 0333
Jan 2007    1514244 2 0395

Isso funciona por:

  • Lendo <> , que é o identificador de arquivo mágico - recebe informações de pipes ou nomes de arquivos.
  • Lemos a primeira linha e transformamos isso em uma matriz de @headers .
  • nós iteramos cada linha adicional e mapeamos os valores separados por vírgulas em um hash (chamado %stuff ).
  • Extraia DOB de %stuff - e processe-o usando strptime/strftime em uma data, conforme necessário.
  • extraia AnimalNumber de %stuff e use um padrão regex para extrair os números que você procura depois
  • porque usamos vários grupos de captura, os elementos capturados são retornados como uma lista, que podemos ficar juntos (com um delimitador de espaço) usando join .

Edit: Porque você está olhando para a classificação - você terá que ler todo o lote primeiro na memória (o que acima não é por motivos de eficiência).

No entanto:

#!/usr/bin/env perl 
use strict;
use warnings;

use Data::Dumper;
use Time::Piece;

my @headers = split( /,/, <> =~ s/\n//r );

my @records;

while (<>) {
    chomp;    #strip linefeed.
    my %stuff;

    #this makes use of the fact we know the headers already
    #so we can map from the line into named columns.
    @stuff{@headers} = split /,/;    #read comma sep into hash

 #DOB:
 #take date, parse it into a unix time, then use strftime to output "Mon year"
    $stuff{'formtime'} =
        Time::Piece->strptime( $stuff{'DOB'}, "%d/%m/%Y" )->strftime("%b %Y");

    #regex match against AnimalNumber, and then join it with space separation.
    #separator
    $stuff{'number_arr'} = [ $stuff{'AnimalNumber'} =~ m/(\d+)(\d)(\d{4})/ ];

    push( @records, \%stuff );
}

foreach
    my $record ( sort { $b->{'number_arr'}->[2] <=> $a->{'number_arr'}->[2] }
    @records )
{
    print join( "\t",
        $record->{'formtime'}, join( " ", @{ $record->{'number_arr'} } ),
        ),
        "\n";
}

Semelhante ao anterior, mas pré-processamos cada registro em uma matriz de hashes e usamos sort da saída antes da impressão - com base no campo "chave" - último grupo de 4 dígitos em number_arr .

    
por 05.10.2015 / 12:39
0

Outra maneira Perl, usando o GNU date :

$ perl -F, -lane 'next if $.==1; $F[0]=~s/IE(\d{7})(\d)(\d{4})/$1 $2 $3/; 
                  $F[1]=~s#(..).(..).(.*)#$2/$1/$3#; 
                  chomp($d='date -d "$F[1]" +"%b %Y"'); 
                  print "$d $F[0]"' file
Feb 2010 1612892 4 0602
Jan 2009 1414244 9 0333
Jan 2007 1514244 2 0395

O -a faz com que perl aja como awk , dividindo sua linha de entrada no caractere dado por -F e salvando-a como a matriz @F . O $F[0]=~s/IE... remove o IE do primeiro campo e divide o restante conforme solicitado. O $F[1]=~s#... irá reformatar a data em MM/DD/YYYY . O chomp(... executará o GNU date , pedindo para retornar o formato Mon YYYY (o chomp remove as novas linhas) que é salvo como $d . Finalmente, $d e o 1º campo modificado são impressos.

    
por 05.10.2015 / 13:14