我的文件中的数据如下所示:
field11|field12|field13
field11|field12|field23
field11|field32|field33
field41|field42|field43
field41|field52|field43
field41|field62|field63
正如你所看到的,我有 2 个 id field11
,并且field41
我希望它们只出现一次,如下所示:
field11|{'field12','field32'}|field13
field41|{'field42','field52','field62'}|field43
我希望第三个字段与该 id 的第一次出现相同。每个 id($1) 都有不同的 $3,而第一个或第一个和第二个字段保持相同。但我需要打印该 id 第一行的内容。如示例所示,我们打印的是 field13 而不是 field23。
我正在尝试用awk
/来实现这一点sed
。我知道一个解决方案可以使用 shell 中的基本循环来实现此目的。但我需要在awk
或一些类似的工具中执行此操作。
答案1
一个稍长(但希望易于理解)的awk
解决方案:
BEGIN { FS = OFS = "|" }
function output() {
if (FNR == 1) return
data = ""
for (i in col2) {
qi = sprintf("'%s'", i);
data = (data == "" ? qi : data "," qi)
}
print col1, sprintf("{%s}", data), col3
}
$1 == col1 && !($2 in col2) { col2[$2] }
$1 != col1 {
output()
col1 = $1; col3 = $3
delete col2; col2[$2]
}
END { output() }
该BEGIN
块只是将输入和输出字段分隔符设置为|
。
该函数将获取(第一列中的 ID)、(第二列中的唯一数据数组)和(第三列中该特定 ID 的第一个数据项)output()
中收集的数据并将其输出。它迭代 中的键,单独引用它们并将它们之间用逗号添加到字符串变量 中。然后它打印, (在大括号内)和。col1
col2
col3
col2
data
col1
data
col3
当我们在第二列中找到该特定 ID 之前从未见过的条目时,就会执行下一个块。它只是将第二列添加为 中的键col2
。
当我们在第一列中找到新 ID 时,执行后面的块。它调用output()
并重置收集的变量以开始收集该新 ID 的数据。
在END
块中,我们调用output()
输出最后一个ID的数据。
该程序不会尝试将整个文件立即存储在内存中,而是要求数据在第一列上排序。
在提供的数据上运行它:
$ awk -f script.awk file
field11|{'field12','field32'}|field13
field41|{'field42','field62','field52'}|field43
答案2
Perl 的一些粗糙之处:
perl -F'\|' -lane '
$f2{ $F[0] }{ $F[1] } = 1;
$f3{ $F[0] } = $F[2] if not exists $f3{ $F[0] };
} END {
for $key (sort keys %f2) {
printf "%s|{%s}|%s\n",
$key,
join(",", map {chr(39) . $_ . chr(39)} sort keys %{$f2{$key}}),
$f3{$key};
}
' file
前两行使用关联数组累积数据。
然后 END 块循环处理数据、格式和打印。