我的文件中有以下数据,其中具有用户和主管关系。
user |supervisor |id
-----|-----------|----
a | b | 1
b | c | 2
c | d | 3
e | b | 4
我想分解用户和主管之间的关系层次结构,如下所示。
user |supervisor |id
-----|-----------|----
a | b | 1
a | c | 1
a | d | 1
b | c | 2
b | d | 2
c | d | 3
e | b | 4
e | c | 4
e | d | 4
如您所见,对于用户“a”,直接主管是“b”,但“b”再次将“c”作为其主管。所以间接地“c”也是“a”的主管,依此类推。例如,我的目标是分解给定用户的任何级别的层次结构。在 Unix 中实现此功能的最佳方法是什么?
答案1
我假设每个用户仅在输入文件(在“用户”列中)中出现一次。我进一步假设竖线 ( |
) 分隔符实际上在文件中,并且它们始终通过空格与数据分隔,并且标题行是不是实际上存在。
这是使用的两遍解决方案awk
。第一遍构建一个包含每个人的主管的数组;第二遍构建输出:
awk 'pass==1 { super[$1] = $3; }
pass==2 {
print
user=$3
while (super[user] != "") {
print $1, "|", super[user], "|", $5
user=super[user]
}
}
' pass=1 data pass=2 data
这将产生未正确对齐的输出。要解决这个问题,请将其通过管道传输column -t
。或者我们可以在脚本中格式化输出awk
;如果需要,请指定所需的格式规则。
顺便说一句,这个操作通常被称为传递闭包。
答案2
复杂的awk解决方案:
awk 'NR<3{ h=(h=="")? $0 : h ORS $0 }NR>2{ uid[$1]=$5; us[$1]=$3 }
END{
print h;
for (u in uid) {
id=uid[u]; spvr=us[u]; printf("%-5s|%-11s|%-4s\n",u,spvr,id);
while (spvr in uid) {
spvr=us[spvr]; printf("%-5s|%-11s|%-4s\n",u,spvr,id)
}
}
}' yourfile
输出:
user |supervisor |id
-----|-----------|----
a |b |1
a |c |1
a |d |1
b |c |2
b |d |2
c |d |3
e |b |4
e |c |4
e |d |4
细节:
NR<3{ h=(h=="")? $0 : h ORS $0 }
- 捕捉标头线uid[$1]=$5
-用户身份关系数组us[$1]=$3
-用户主管关系数组spvr=us[u]
- 第 1 个导师对于当前的用户while (spvr in uid) { ... }
- 尽管导师是在用户列表,获取父级导师
答案3
我的 awk 解决方案(使用 RomanPerekhrest 的输出格式)。基本上,有两个相关的循环。首先,如果为用户处理新的主管,则必须将该主管的所有依赖项(即主管链)添加到该用户。之后,第二个循环查找所有其他用户,这些用户将当前处理的用户作为依赖项,并将当前用户的所有依赖项添加到其中:
#!/usr/bin/awk
# file process_it.awk
BEGIN {
FS="|";
}
NR<3 {
h=(h==""? $0 : h ORS $0)
}
NR>2 {
gsub(/ /, "", $0)
curr_user=$1;
curr_supervisor=$2;
curr_id=$3;
print curr_user, curr_supervisor;
arr[curr_user][curr_supervisor]++;
id[curr_user]=curr_id;
if(isarray(arr[curr_supervisor])) {
for(sub_indx in arr[curr_supervisor])
arr[curr_user][sub_indx]++;
}
else
delete arr[curr_supervisor];
for(indx in arr) {
if(isarray(arr[indx])) {
for(sub_indx in arr[indx]) {
if(sub_indx==curr_user) {
for(sub_indx2 in arr[curr_user])
arr[indx][sub_indx2]++;
}
}
}
}
}
END {
print h;
for(i in arr) {
if(isarray(arr[i])) {
for(j in arr[i])
printf "%-5s|%-11s|%-3s\n", i, j, id[i];
}
}
}
使用:
awk -f process_it.awk your_file.txt