将多个CSV行转换为单个列

将多个CSV行转换为单个列,csv,awk,scripting-language,Csv,Awk,Scripting Language,我有一个以下格式的CSV文件: #时间、CPU、数据 x、 0,a x、 1,b y、 0,c y、 1,d 我想把它变成这个 时间,CPU 0数据,CPU 1数据 x、 a,b y、 c,d 但是我不知道一个系统中会有多少个CPU核(由CPU列表示)。我还有多列数据(不仅仅是单列数据) 我该怎么做呢 示例输入 #主机名、间隔、时间戳、CPU、%user、%nice、%system、%iowait、%steal、%idle 主机名,6002018-07-24 00:10:01 UTC,-1,

我有一个以下格式的CSV文件:

#时间、CPU、数据
x、 0,a
x、 1,b
y、 0,c
y、 1,d
我想把它变成这个

时间,CPU 0数据,CPU 1数据 x、 a,b y、 c,d 但是我不知道一个系统中会有多少个CPU核(由CPU列表示)。我还有多列数据(不仅仅是单列数据)

我该怎么做呢

示例输入
#主机名、间隔、时间戳、CPU、%user、%nice、%system、%iowait、%steal、%idle
主机名,6002018-07-24 00:10:01 UTC,-1,5.19,0,1.52,0.09,0.13,93.07
主机名,6002018-07-24 00:10:01 UTC,0,5.37,0,1.58,0.15,0.15,92.76
主机名,6002018-07-24 00:10:01 UTC,1,8.36,0,1.75,0.08,0.1,89.7
主机名,6002018-07-24 00:10:01 UTC,2,3.87,0,1.38,0.07,0.12,94.55
主机名,6002018-07-24 00:10:01 UTC,3,3.16,0,1.36,0.05,0.14,95.29
主机名,6002018-07-24 00:20:01 UTC,-1,5.13,0,1.52,0.08,0.13,93.15
主机名,6002018-07-24 00:20:01 UTC,0,4.38,0,1.54,0.13,0.15,93.8
主机名,6002018-07-24 00:20:01 UTC,1,5.23,0,1.49,0.07,0.11,93.09
主机名,6002018-07-24 00:20:01 UTC,2,5.26,0,1.53,0.07,0.12,93.03
主机名,6002018-07-24 00:20:01 UTC,3,5.64,0,1.52,0.04,0.12,92.68
这将是这个文件的输出:(CPU-1变成CPU ALL)(而且键值只是时间戳(主机名和间隔保持不变)

主机名、时间间隔、时间戳、CPU ALL%user、CPU ALL%nice、CPU ALL%system、CPU ALL%iowait、CPU ALL%TEAL、CPU ALL%idle、CPU 0%user、CPU 0%nice、CPU 0%iowait、CPU 0%TEAL、CPU 0%idle、CPU 1%user、CPU 1%iowait、CPU 1%idle、CPU 2%nice、CPU 2%system、CPU 2%iowait、CPU 2%TEAL%idle,CPU 3%用户,CPU 3%尼斯,CPU 3%系统,CPU 3%iowait,CPU 3%窃取,CPU 3%空闲 主机名,6002018-07-24 00:10:01 UTC,5.19,0,1.52,0.09,0.13,93.07,5.37,0,1.58,0.15,92.76,8.36,0,1.75,0.08,0.1,89.7,3.87,0,1.38,0.07,0.12,94.55,3.16,0,1.36,0.05,0.14,95.29 主机名,6002018-07-24 00:20:01 UTC,5.13,0,1.52,0.08,0.13,93.15,4.38,0,1.54,0.13,0.15,93.8,5.23,0,1.49,0.07,0.11,93.09,5.26,0,1.53,0.07,0.12,93.03,5.64,0,1.52,0.04,0.12,92.68
您的问题不清楚,也不包含您发布的更大/可能更真实的CSV示例的预期输出,因此请确定您希望获得的输出,但这至少会向您展示正确的方法:

$ cat tst.awk
BEGIN{
    FS = OFS = ","
}
NR==1 {
    for (i=1; i<=NF; i++) {
        fldName2nmbr[$i] = i
    }
    tsFldNmbr  = fldName2nmbr["timestamp"]
    cpuFldNmbr = fldName2nmbr["CPU"]
    next
}
{
    tsVal  = $tsFldNmbr
    cpuVal = $cpuFldNmbr

    if ( !(seenTs[tsVal]++) ) {
        tsVal2nmbr[tsVal]  = ++numTss
        tsNmbr2val[numTss] = tsVal
    }

    if ( !(seenCpu[cpuVal]++) ) {
        cpuVal2nmbr[cpuVal]  = ++numCpus
        cpuNmbr2val[numCpus] = cpuVal
    }

    tsNmbr  = tsVal2nmbr[tsVal]
    cpuNmbr = cpuVal2nmbr[cpuVal]

    cpuData = ""
    for (i=1; i<=NF; i++) {
        if ( (i != tsFldNmbr) && (i != cpuFldNmbr) ) {
            cpuData = (cpuData == "" ? "" : cpuData OFS) $i
        }
    }
    data[tsNmbr,cpuNmbr] = cpuData
}
END {
    printf "%s", "timestamp"
    for (cpuNmbr=1; cpuNmbr<=numCpus; cpuNmbr++) {
        printf "%sCPU %s Data", OFS, cpuNmbr2val[cpuNmbr]
    }
    print ""

    for (tsNmbr=1; tsNmbr<=numTss; tsNmbr++) {
        printf "%s", tsNmbr2val[tsNmbr]
        for (cpuNmbr=1; cpuNmbr<=numCpus; cpuNmbr++) {
            printf "%s\"%s\"", OFS, data[tsNmbr,cpuNmbr]
        }
        print ""
    }
}

我将每个CPU的数据放在双引号内,这样您就可以将其导入Excel或类似文件,而不用担心子字段之间的逗号。

如果我们假设CSV输入文件是根据不断增加的时间戳排序的,您可以尝试以下操作:

use feature qw(say);
use strict;
use warnings;

my $fn = 'log.csv';
open ( my $fh, '<', $fn ) or die "Could not open file '$fn': $!";
my $header = <$fh>;
my %info;
my @times;
while ( my $line = <$fh> ) {
    chomp $line;
    my ( $time, $cpu, $data ) = split ",", $line;
    push @times, $time if !exists $info{$time};
    push @{ $info{$time} }, $data;
}
close $fh;

for my $time (@times) {
    say join ",", $time, @{ $info{$time} };
}

@HåkonHægland我不小心打错了示例。我的错。@Ed Morton我现在将添加csv的输出我已从“示例输出”中删除了伪换行符但是我不得不猜测。请检查以确保它是正确的。你应该注意你发布的内容,因为一个字符可以决定程序是否正常工作。@STemma:你很幸运对此有任何回应。在没有任何证据表明你自己试图解决这个问题的情况下发布一个要求,并滥发所有的邮件在标签中编写你所知道的语言是非常糟糕的形式。Stack Overflow不是一个普通的帮助论坛,或者根本不是一个论坛。你应该把你的问题想象成WikMedia上的一个新页面,内容应该是独特的和经过充分研究的。让其他人为你做工作不是我们的目的之一。请大家发表评论g:请不要在评论中重复个人意见,也不要盲目地投赞成票或反对票。我应该能够做到这一点。问题不清楚是我的错,所以我会让问题更清楚,但谢谢你的回答。
use feature qw(say);
use strict;
use warnings;

my $fn = 'log.csv';
open ( my $fh, '<', $fn ) or die "Could not open file '$fn': $!";
my $header = <$fh>;
my %info;
my @times;
while ( my $line = <$fh> ) {
    chomp $line;
    my ( $time, $cpu, $data ) = split ",", $line;
    push @times, $time if !exists $info{$time};
    push @{ $info{$time} }, $data;
}
close $fh;

for my $time (@times) {
    say join ",", $time, @{ $info{$time} };
}
x,a,b
y,c,d