请帮我将行值转换为列值。我的输入file.dat
是:
前两个字段分别是字母字符串和数字,输入将始终具有相同数量的字段,即6。
输入
A|1|DLT|07:30|10:30|34
A|1|STG|07:30|10:30|NA
A|1|MAIN|06:30|10:30|NA
A|2|STG|07:30|10:30|NA
A|2|UNLD|04:30|10:30|90
B|1|DLT|03:30|10:30|34
B|1|STG|07:30|09:30|NA
B|1|MAIN|07:25|10:30|NA
B|1|UNLD|05:30|12:30|8
输出有变化。输出字段所有行有 18 个字段,每行将有输入文件的前两个字段,后跟按以下顺序排列的第 3 个、...、第 18 个字段,其中空白字段是输入文件中缺少的行,记录顺序为 DLT, STG,MAIN,UNLD 后面有相应的记录。
输出
A|1|DLT|07:30|10:30|34|STG|07:30|10:30|NA|MAIN|06:30|10:30|NA||||
A|2|||||STG|07:30|10:30|NA|||||UNLD|04:30|10:30|90
B|1|DLT|03:30|10:30|34|STG|07:30|09:30|NA|MAIN|07:25|10:30|NA|UNLD|05:30|12:30|8
- A|1 缺少 UNLD
- A|2 缺少 DLT 和 MAIN
- 没有任何记录丢失
类似地,如果与输入文件的第三个字段对应的任何记录丢失,则必须将其替换为空白值,以便输出字段将具有18个领域。
答案1
我的awk
建议:
awk -F'|' '{ if (a[$1$2] == "") {
a[$1$2] = $0
}
else {
a[$1$2] = a[$1$2]","$3"|"$4"|"$5"|"$6
}
}
END {
for (key in a) {
print a[key]
}
}' <input.txt | sort
解释
这-F'|'
选项定义了字段分隔符(awk
用于解析行中的字段)作为字符“|”,因为这就是文件的格式。
a[...]
是一个数组。数组在awk
有点像 python 字典,而不是索引键这实际上是字符串。对于输入文件的每一行,测试都会检查与前 2 个字段(例如第一行)if (a[$1$2] == "")
对应的键(如果有条目)。$1$2 = A1
如果不是(A|1|...
读取第一行),则整行存储在此键 ( a[$1$2] = $0
) 中。如果已经有内容(A|1|...
已经存储了另一行),那么我们用逗号连接该条目,并用“|”分隔从 3 到 6 的字段。 ( a[$1$2] = a[$1$2]","$3"|"$4"|"$5"|"$6
)。
最后,当我们浏览完文件后,我们需要输出每个键的条目。我们在一个END
块中执行此操作(一旦读取了所有文件,就会执行这些指令)。为此,我们只需遍历数组 ( for (key in a)
) 的所有键并打印每个键的条目即可。
然后,最终输出通过管道传输到sort
,因为awk
不会按字母数字顺序遍历数组键,因此对输出进行排序会更干净,以便您得到A|1|...
后面的行,A|2|...
依此类推。
你的最后一次编辑让整个事情变得有点棘手。所需的说明awk
会变得有点复杂,所以我建议您创建一个脚本文件(创建一个扩展名为 例如 的awk
文本文件)。将以下脚本复制到其中:.awk
myScript.awk
BEGIN { FS="|" }
$3 == "DLT" {
dlt[$1"|"$2]=$3"|"$4"|"$5"|"$6
a[$1"|"$2]++
}
$3 == "STG" {
stg[$1"|"$2]=$3"|"$4"|"$5"|"$6
a[$1"|"$2]++
}
$3 == "MAIN" {
main[$1"|"$2]=$3"|"$4"|"$5"|"$6
a[$1"|"$2]++
}
$3 == "UNLD" {
unld[$1"|"$2]=$3"|"$4"|"$5"|"$6
a[$1"|"$2]++
}
END {
for (key in a) {
if (dlt[key] == "") dlt[key]="|||"
if (stg[key] == "") stg[key]="|||"
if (main[key] == "") main[key]="|||"
if (unld[key] == "") unld[key]="|||"
print key"|"dlt[key]"|"stg[key]"|"main[key]"|"unld[key]
}
}
使用它:
awk -f myScript.awk <input.txt | sort
如果你理解了我最初答案的解释,你应该能够理解这个算法。这次我们为每种数据类型(dlt、stg、main 和 unld)创建一个数组,并将它们的值存储在与前两个字段对应的键处。数组“ a
”用于跟踪所有可能的键。最后我们遍历 array 的键a
,如果其中一个数据数组在该键处为空,则填充“|||”如你所愿,这样每行都有 18 个字段。
答案2
略有不同瓦伦丁·B.的回答:
awk -F"|" '
{
key = $1 "|" $2
a[key] = 1
b[key][$3] = $3 "|" $4 "|" $5 "|" $6
}
END {
subtype[1] = "DLT"
subtype[2] = "STG"
subtype[3] = "MAIN"
subtype[4] = "UNLD"
for (key in a)
{
output = key
for (i = 1; i <= 4; i++)
{
st = subtype[i]
if (b[key][st] == "")
output = output "||||"
else
output = output "|" b[key][st]
}
print output
}
}' file.dat
如在瓦伦丁·B.的回答:
-F"|"
将字段分隔符设置为|
,以便我们可以从输入行中提取字段。key
设置为A|1
、A|2
、 或B|1
(或者通常是前两个字段的串联)。我们使用它来组合与该组合键相关的(最多)四行数据。- 设置
a[key]
为 1 以记录我们看到的数据(键)。 - 设置
b[key][$3]
为键后该行的其余部分。例如,b["A|1"]["DLT"]
将被设置为"DLT|07:30|10:30|34"
.这样,我们就可以记录全部我们看到的数据(假设行不超过六个字段,如问题中指定)。请注意,我们在不知道 中可能包含什么的情况下执行此操作$3
。
当我们读取完所有数据后:
- 定义子类型的数组(即有效值
$3
,“DLT”、“STG”、“MAIN”和“UNLD”),因此我们不需要多次列出它们。 - 迭代我们见过的所有键。开始构建输出线。
- 迭代四个子类型(上面列出)。如果我们有该键和该子类型的数据,请将其附加到输出行;否则,附加四个空白字段。
- 打印该行。
sort
如果您希望对输出进行排序,请将其通过管道传入。 (我说“如果”是因为,虽然问题节目示例输出已排序,但并不表示输出必须已排序。)
这个问题对于第二个字段中的内容并不精确;它只说它们是“数字”。如果它们可能是具有不同位数的整数,并且您希望它们按数字排序,请使用
sort -t"|" -k1,1 -k2,2n
答案3
捕获第四个字段中的值并将其存储在名为“val”的变量中
创建一个数组并根据第一个和第二个字段附加数组的值。
最后,打印数组值并进行小的调整(替换、删除等)
$ awk -F\| '{ val=$3;
for (i=4;i<=NF;i++) {val=val"|"$i}
Arr[$1OFS$2]=Arr[$1OFS$2]","val;
next
}
END { for (i in Arr) {
A=Arr[i];
sub(" ","|",i);
print i,A
}
}' test.txt | sed "s/ ,/\|/"
A|1|DLT|07:30|10:30|34 ,STG|07:30|10:30|NA ,MAIN|06:30|10:30|NA
B|1|DLT|03:30|10:30|34 ,STG|07:30|09:30|NA ,MAIN|07:25|10:30|NA ,UNLD|05:30|12:30|8
A|2|UNLD|04:30|10:30|90 ,DLT|08:30|11:30|4 ,STG|07:30|10:30|NA