将第一行时间戳附加到下面的列表中

将第一行时间戳附加到下面的列表中

尝试获取如下所示的输入:

08/22/2019 12:00:58
Name Cans Bucks Puns
Clyde 12 2 79
Sheila 32 16 42
Elmo 44 18 21

08/23/2019 19:00:22
Name Cans Bucks Puns
Clyde 18 21 46
Sheila 37 2 11
Elmo 41 3 10

像这样的输出:

name=Clyde cans=12 bucks=2 puns=79 ts=1566475258
name=Sheila cans=32 bucks=16 puns=42 ts=1566475258
name=Elmo cans=44 bucks=18 puns=21 ts=1566475258
name=Clyde cans=18 bucks=21 puns=46 ts=1566586822
name=Sheila cans=37 bucks=2 puns=11 ts=1566586822
name=Elmo cans=41 bucks=3 puns=10 ts=1566586822

我尝试用 awk 来实现这一点,但没有成功(减去让我完全难住的时间转换)。

我得到的最接近的是:

ts=08/22/2019 12:00:58
name=Clyde cans=12 bucks=2 puns=79
name=Sheila cans=32 bucks=16 puns=42
name=Elmo cans=44 bucks=18 puns=21
ts=08/23/2019 19:00:22
name=Clyde cans=18 bucks=21 puns=46
name=Sheila cans=37 bucks=2 puns=11
name=Elmo cans=41 bucks=3 puns=10

我什至不确定 awk 是最好的工具。

答案1

awk -F'[/: ]' '{
  if (NF==6){
    ts=mktime($3" "$1" "$2" "$4" "$5" "$6)
    skipheader=1
  }
  else if (NF==0 || skipheader){
    skipheader=0
  }
  else {
    print "name="$1,"cans="$2,"bucks="$3,"puns="$4,"ts="ts
  }
}' file
  • /使用,和空格字符拆分字段:以获取单独的日期和时间部分。
  • 如果字段数为 6,则创建时间戳ts并设置一个标志以跳过下一个标题行。
  • 如果字段数为零或skipheader设置了标志,则重置skipheader标志。
  • 否则,打印数据。

输出:

name=Clyde cans=12 bucks=2 puns=79 ts=1566468058
name=Sheila cans=32 bucks=16 puns=42 ts=1566468058
name=Elmo cans=44 bucks=18 puns=21 ts=1566468058
name=Clyde cans=18 bucks=21 puns=46 ts=1566579622
name=Sheila cans=37 bucks=2 puns=11 ts=1566579622
name=Elmo cans=41 bucks=3 puns=10 ts=1566579622

答案2

以下 perl 脚本将适用于输入中任意数量的字段以及任意字段名称。

它需要时间::日期列表::更多实用工具库模块。这两个可能已经为您的发行版打包了(例如在 debian 上sudo apt-get install libtimedate-perl liblist-moreutils-perl)。可以编写脚本,使其不需要这些模块,但是当现有的可重用库代码完全可以完成您需要的工作时,就不需要重新发明轮子。

该脚本假定输入行由任意数量的空格(即一个或多个空格、制表符等)分隔。如果输入是制表符分隔的,请将行更改split;split /\t/;。如果第一个字段中的任何名称包含空格(例如Firstname Surname),或者任何字段名称包含空格,则制表符分隔符将是一个好主意。

如果您需要硬编码特定时区,例如GMT,更改以下行:

    $ts = str2time($_);

例如(注意前面引号内的空格GMT):

    $ts = str2time($_ . ' GMT');
#!/usr/bin/perl

use strict;
use Date::Parse;
use List::MoreUtils qw(pairwise);

my @columns;
my $ts='';

while(<>) {
  s/^\s*|\s*$//g;    #/ strip leading and trailing spaces
  next if (/^$/);    #/ skip empty lines
  chomp;

  # line begins with two digits and a slash? it's a date.
  if (m/^\d\d\//) {
    # get the date and parse it so that we have seconds since the epoch
    $ts = str2time($_);

    # get the next line and split it into column headers
    $_ = readline;
    @columns = split;

  } else {
    # split the current line into @row array
    my @row=split;
    # use pairwise() function from List::MoreUtils module to merge the
    # @columns and @row arrays.
    print join(" ", (pairwise { "$a=$b" } @columns, @row), "ts=$ts"), "\n";
  }

}

示例输出:

$ ./reformat.pl input.txt 
Name=Clyde Cans=12 Bucks=2 Puns=79 ts=1566439258
Name=Sheila Cans=32 Bucks=16 Puns=42 ts=1566439258
Name=Elmo Cans=44 Bucks=18 Puns=21 ts=1566439258
Name=Clyde Cans=18 Bucks=21 Puns=46 ts=1566550822
Name=Sheila Cans=37 Bucks=2 Puns=11 ts=1566550822
Name=Elmo Cans=41 Bucks=3 Puns=10 ts=1566550822

注意:如果任何数据行的列数多于或少于列标题的数量,这不会完全破坏,但会产生异常的输出。对于缺失的字段,它只会打印带有=符号但没有值的字段名称(例如,如果输入行只有 2 个字段,它将输出),如果有更多字段,它只会打印以(Puns=为前缀的值=例如,如果输入行有一个值为 20 的额外字段,它将输出=20)。

例如,如果您的示例输入有第三个数据块,如下所示:

08/23/2019 23:30:01
Name Cans Bucks Puns
Clyde 18 21 46
Sheila 37 2 11
Elmo 41 3 10
Missing 41 3
Extra 41 3 10 20

这将导致额外的输出:

Name=Clyde Cans=18 Bucks=21 Puns=46 ts=1566567001
Name=Sheila Cans=37 Bucks=2 Puns=11 ts=1566567001
Name=Elmo Cans=41 Bucks=3 Puns=10 ts=1566567001
Name=Missing Cans=41 Bucks=3 Puns= ts=1566567001
Name=Extra Cans=41 Bucks=3 Puns=10 =20 ts=1566567001

相关内容