|
각 행에 가변 개수의 열/필드가 있을 수 있는 파이프로 구분된( ) 파일이 있습니다 . 처음 두 필드와 마지막 두 필드만 항상 존재하며, 사이에는 최대 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;
}