awk/sed シェルスクリプトヘルプ

awk/sed シェルスクリプトヘルプ

以下の表の情報(偽情報)を使用してスクリプトを作成する必要があります。

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,

基本的には生年月日だけ書けばいいのですがanimalnumber、動物番号はこう分けなければなりません。

IE161289240602しなければならない1612892 4 0602

そして、生まれた月と年だけをリストする必要があるので、最初の行は次のようになります。

Feb 2010 1412892 4 0602

これを行う方法についてのアイデアはありますか?これは私の能力の範囲を少し超えているようです。

答え1

~のため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
  • mktimeEPOCH形式の文字列からタイムスタンプ(秒単位)を生成するYYYY MM DD HH MM SS
  • strftimeタイムスタンプを必要な形式に変換します(の場合%b %Y)。
  • gensub最初のフィールドの数字()を末尾のスペース8()自体()で置き換えます。[0-9]$1&

文字列形式のみを見ることができるので、次のものを使用できます。sed:

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

またはsedいいえ金利注文する

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

または

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

答え2

私は「パールを使用」したいと思います:

#!/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";
}

この出力は次のようになります。

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

これは次のように達成されます。

  • マジックファイルハンドルの読み取り<>- パイプまたはファイル名から入力を取得します。
  • 最初の行を読んでこれを@headers
  • 各追加行を繰り返し、コンマで区切られた値をハッシュ(という)にマッピングします%stuff
  • DOB- から抽出し、必要に応じて日付として%stuff処理します。strptime/strftime
  • 正規表現パターンを抽出し、AnimalNumber使用%stuffして目的の数を抽出します。
  • 複数のキャプチャグループを使用しているため、キャプチャされた要素はリストとして返され、空白区切り文字を使用して一緒に貼り付けることができますjoin

編集:ソートを検討しているので、まずデータ全体をメモリに読み込む必要があります(効率性の理由から上記では行われません)。

しかし:

#!/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";
}

上記と似ていますが、各レコードをハッシュ配列に前処理してsortから、印刷する前に「キー」フィールドに基づいて出力の最後の4桁のセットを使用してくださいnumber_arr

答え3

GNUを使用する別のPerl方式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

makeは、与えられた文字に基づいて入力行を分割し、それを配列として保存するなどのことを-a行います。最初のフィールドから削除し、必要に応じて残りのフィールドを分割します。は日付形式を再指定し、GNUを実行して別の名前で保存を返すように要求します。最後に変更された最初のフィールドを印刷します。perlawk-F@F$F[0]=~s/IE...IE$F[1]=~s#...MM/DD/YYYYchomp(...dateMon YYYYchomp$d$d

関連情報