我有一个用管道分隔的 ( |
) 文件,其中每行可以有可变数量的列/字段。只有前两个字段和最后两个字段始终存在,中间可以有变化(但偶数)数量的字段,最多 10 个,因此该行总共最多有 14 个字段 (2 + 0 ... 10 + 2).
目标是替换“缺失”字段并将文件转换为每行具有固定列数的文件。
C_A
“变量”字段的特点是始终由一个...形式的“索引键”C_E
和一个值组成。
输入示例:
10|100|C_A|val_18|C_D|val_20|50|60
40|200|C_A|val_5|C_B|val_10|C_C|val_30|C_D|val_90|C_E|val_83|40|45
80|100|C_E|val_90|50|60
这是预期的输出:
10|100|C_A|val_18|||||C_D|val_20|||50|60
40|200|C_A|val_5|C_B|val_10|C_C|val_30|C_D|val_90|C_E|val_83|40|45
80|100|||||||||C_E|val_90|50|60
答案1
GNU awk
我们可以通过将名称与字段索引映射来解决这个问题。
- C_A => $3
- C_B => $5 ...等等
$ awk '
BEGIN {
OFS = FS = "|"
n = split("A-B-C-D-E", x, "-")
for (i=1; i<=n; ++i) h["C_" x[i]] = 2*(i-1) + 3
}
{
# record which out of
# ca/cb/.../ce present
for (i=3; i<NF-2; i+=2) seen[$i] = $(i+1)
# store fields in preparation
# for re-filling them based on seen
nf = split($0, f, FS); $0=""
# fill up the first two..easy does it
$(1) = f[1]
$(2) = f[2]
# recall which fields c_?
# were seen then fill up
# corresponding field and field value
for (var in h) {
i = h[var]
if (var in seen) {
$(i) = var
$(i+1) = seen[var]
} else { $(i) = $(i+1) = ""}
}
# append the last two fields
$(NF+1) = f[nf-1]
$(NF+1) = f[nf]
# above line **NOT** a typo
# clear out the array seen
# for the next iteration
split("", seen, ".")
}1
' file
结果:
10|100|C_A|val_18|||||C_D|val_20|||50|60
40|200|C_A|val_5|C_B|val_10|C_C|val_30|C_D|val_90|C_E|val_83|40|45
80|100|||||||||C_E|val_90|50|60
答案2
将变量字段读入数组,打印数组,当不在一行中使用时生成空字符串(不要忘记-F '|'
):
BEGIN{
# initialize expected keys for file (value 1 not used)
all_keys["C_A"]=1;
all_keys["C_B"]=1;
all_keys["C_C"]=1;
all_keys["C_D"]=1;
all_keys["C_E"]=1;
}
{
# fill arrays for line
for(i=3;i<NF-1;i+=2) {
key[$i]=$i;
value[$i]=$i+1;
}
# first two fields
printf $1"|"$2"|";
# all variable key/value pairs, occurring on line or not
for (k in all_keys) {
printf key[k]"|"value[k]"|";
}
# last two fields
print $NF-1"|"$NF;
# delete arrays so we don't carry over values into the next line
delete key;
delete value;
}