awk 搜索然后将行转置为列

awk search then transpose rows to columns

对 awk 和数据操作很陌生,但遇到困难,正在寻求帮助。

有一个文件:Ntab.txt是两台主机的示例文件,真实文件中有很多台主机一个接一个。

每个主机有多个 "displayName"s(地址),每个 'displayName'.

都有相应的数字

>cat Ntab.txt
name    devtwr1
displayName     00:67:BB
capacityInKB    104,857,600
consumedCapacityInKB    4,042,752
dpPoolID        20
displayName     00:7B:FD
capacityInKB    52,428,800
consumedCapacityInKB    14,880,768
dpPoolID        10
displayName     00:7C:28
capacityInKB    34,179,712
consumedCapacityInKB    29,804,544
dpPoolID        20
displayName     00:7C:29
capacityInKB    34,179,712
consumedCapacityInKB    5,462,016
dpPoolID        20
name    devtwr2
displayName     00:67:BB
capacityInKB    104,857,600
consumedCapacityInKB    4,042,752
dpPoolID        20
displayName     00:7B:FD
capacityInKB    52,428,800
consumedCapacityInKB    14,880,768
dpPoolID        10
displayName     00:7C:28
capacityInKB    34,179,712
consumedCapacityInKB    29,804,544
dpPoolID        20
displayName     00:7C:29
capacityInKB    34,179,712
consumedCapacityInKB    5,462,016
dpPoolID        20

我需要能够在每个 'name'(主机)之后将行中的 $2 数据生成为列,并且采用 csv 类型格式,标题可选。我不能使用 (,:) 作为分隔符,因为数据包含它们(制表符或 ;)。

喜欢:

name;displayName;capacityInKB;consumedCapacityInKB;dpPoolID        
devtwr1;00:67:BB;104,857,600;4,042,752;20
 ;00:7B:FD;52,428,800;14,880,768;10
 ;00:7C:28;34,179,712;29,804,544;20
 ;00:7C:29;34,179,712;5,462,016;20
devtwr2;00:67:BB;104,857,600;4,042,752;20
 ;00:7B:FD;52,428,800;14,880,768;10
 ;00:7C:28;34,179,712;29,804,544;20
 ;00:7C:29;34,179,712;5,462,016;20

希望有人能提供帮助。

我相信这会满足您的要求:

$ awk '=="name"{name=}  ~/^(displayName|capacityInKB|consumedCapacityInKB)$/{out=out";"} =="dpPoolID"{print name out";"; name=" "; out=""}' Ntab.txt
devtwr1;00:67:BB;104,857,600;4,042,752;20
 ;00:7B:FD;52,428,800;14,880,768;10
 ;00:7C:28;34,179,712;29,804,544;20
 ;00:7C:29;34,179,712;5,462,016;20
devtwr2;00:67:BB;104,857,600;4,042,752;20
 ;00:7B:FD;52,428,800;14,880,768;10
 ;00:7C:28;34,179,712;29,804,544;20
 ;00:7C:29;34,179,712;5,462,016;20

工作原理

awk 一次阅读每一行。每行被分成字段。第一个字段称为 </code>,第二个字段称为 <code>.

  • =="name"{name=}

    这会捕获变量 name 中的名称。

  • ~/^(displayName|capacityInKB|consumedCapacityInKB)$/{out=out";"}

    当我们遇到 displayNamecapacityInKBconsumedCapacityInKB 的任何一行时,将其值附加到变量 out.

  • =="dpPoolID"{print name out";"; name=" "; out=""}

    当我们到达 dpPoolID 行时,打印出收集的值。将 out 重置为空并将 name 设置为 space.

保留每一行的名称

作为另一种输出格式,我们可以保留名称并将其显示在每一行中:

$ awk '=="name"{name=}  ~/^(displayName|capacityInKB|consumedCapacityInKB)$/{out=out";"} =="dpPoolID"{print name out";"; out=""}' Ntab.txt
devtwr1;00:67:BB;104,857,600;4,042,752;20
devtwr1;00:7B:FD;52,428,800;14,880,768;10
devtwr1;00:7C:28;34,179,712;29,804,544;20
devtwr1;00:7C:29;34,179,712;5,462,016;20
devtwr2;00:67:BB;104,857,600;4,042,752;20
devtwr2;00:7B:FD;52,428,800;14,880,768;10
devtwr2;00:7C:28;34,179,712;29,804,544;20
devtwr2;00:7C:29;34,179,712;5,462,016;20

您要求awk,如果适合您,应该接受 John1024 的解决方案。但这是我会用 Perl 代替的那种问题。这是该语言的一种解决方案。它有一点优势(IMO),因为它不依赖于每个记录中字段的特定顺序,除了 displayName 表示新集合的开始。

$ perl -lane '
BEGIN {
  @fields = qw(name displayName capacityInKB consumedCapacityInKB dpPoolID);
  print join ";", @fields;
}
if (/^(name|displayName)/ && $data{displayName}) {
  print join ";", @data{@fields};
  %data = ( name => $data{name} );
}
$data{$F[0]} = $F[1];
END {
  print join ";",@data{@fields};
}' ntab.txt

输出:

name;displayName;capacityInKB;consumedCapacityInKB;dpPoolID
devtwr1;00:67:BB;104,857,600;4,042,752;20
devtwr1;00:7B:FD;52,428,800;14,880,768;10
devtwr1;00:7C:28;34,179,712;29,804,544;20
devtwr1;00:7C:29;34,179,712;5,462,016;20
devtwr2;00:67:BB;104,857,600;4,042,752;20
devtwr2;00:7B:FD;52,428,800;14,880,768;10
devtwr2;00:7C:28;34,179,712;29,804,544;20
devtwr2;00:7C:29;34,179,712;5,462,016;20