![根据重复值合并列表](https://linux22.com/image/129216/%E6%A0%B9%E6%8D%AE%E9%87%8D%E5%A4%8D%E5%80%BC%E5%90%88%E5%B9%B6%E5%88%97%E8%A1%A8.png)
我对数据进行了这样的排序:
a
a f
b
c
c e
d
f z
本质上,这些行都是同一事物的别名列表,它们需要合并。这是简化的。如果在实际情况下很重要,我正在处理已移动的文件路径,并且需要知道哪些文件路径本质上是相同的。输入有 1 列用于初始文件,2 列用于文件重命名。寻找这样的输出:
a f z
b
c e
d
这是针对典型 Linux 系统上的 bash 脚本,因此任何大多数标准工具都可以。到目前为止,我已经尝试了处理该主题的其他问题中的一些 awk 脚本,但没有找到好的结果。
答案1
Awk
解决方案:
awk '{
if (NF == 2) {
if ($1 in r) {
a[r[$1]] = a[r[$1]] OFS $2; next
}
a[$1] = $2; r[$2] = $1;
}
else a[$1];
}
END{ for (i in a) print i, a[i] }' file
NF == 2
- 指示具有 2 个字段的记录的条件(NF
- 字段总数)a
- 包含“独立”文件名(尚未重命名)的数组,例如初始文件名与其重命名版本之间的和或关系(b
例如)d
a -> f
r
- 包含相反关系的数组“重命名的文件名”->“初始文件名”(例如f -> a
)
输出:
a f z
b
c e
d
如果某些文件名可能被重命名多次 - 使用以下扩展解决方案:
awk '{
if (NF == 2) {
if ($1 in r) {
a[r[$1]] = a[r[$1]] OFS $2; r[$2] = r[$1];
}
else { a[$1] = $2; r[$2] = $1 }
}
else a[$1];
}
END{ for (i in a) print i, a[i] }' file
答案2
gawk '
{
arr[cnt][0] = $1
arr[cnt++][1] = $2
}
END {
for(i = 0; i < cnt; i++) {
if(!arr[i][0]) continue
next_name = arr[i][0]
for(j = i; j < cnt; j++) {
if(arr[j][0] != next_name) continue
if(arr[j][1]) {
next_name = arr[j][1]
delete arr[j]
}
printf "%s ", next_name
}
print ""
}
}' cnt=0 input.txt
输入(测试复杂)
u
a
a f
b
c
c e
d
c
f g
g a
a i
i j
a
a z
z w
输出
u
a f g a i j
b
c e
d
c
a z w