有关 awk / sed shell 脚本的帮助

有关 awk / sed shell 脚本的帮助

我必须使用下表中的信息制作一个脚本(假信息)

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
  • mktime从 EPOCH 格式的字符串生成时间戳(以秒为单位)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没有e命令

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

我会想“使用 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";
}

这输出:

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在打印之前使用输出 - 基于“key”字段 - 中的最后一组 4 位数字number_arr

答案3

另一种 Perl 方式,使用 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

make-aperl作用类似于awk根据 给出的字符分割其输入行-F并将其保存为数组@F$F[0]=~s/IE...从第一个字段中删除并IE根据要求拆分其余字段。会将$F[1]=~s#...日期重新格式化为MM/DD/YYYY.将chomp(...运行 GNU ,要求date它返回保存为.最后,打印修改后的第一个字段。Mon YYYYchomp$d$d

相关内容