bash - 根据匹配列合并多个文件

标签 bash awk

我有很多文件(我以5个为例)

如果与第一个文件不匹配,则应在输出中附加 0

文件1

1001 1 2
1002 1 2
1003 3 5
1004 6 7
1005 8 9
1009 2 3

文件2

1002 7
1003 8

文件3

1001 5
1002 3

文件4

1002 10
1004 60
1007  4

文件5

1001 102
1003 305
1005 809

需要的输出

1001 1 2 0 5  0 102
1002 1 2 7 3 10   0
1003 3 5 8 0  0 305
1004 6 7 0 0 60   0
1005 8 9 0 0  0 809
1007 0 0 0 0  4   0
1009 2 3 0 0  0   0

使用下面的代码我可以合并两个文件,但是如何合并所有文件

awk 'FNR==NR{a[$1]=$2;next}{print $0,a[$1]?a[$1]:"0"}' file2 file1

1001 1 2 0
1002 1 2 7
1003 3 5 8
1004 6 7 0
1005 8 9 0

提前致谢

最佳答案

GNU 加入救援!

$ join -a1 -a2 -e '0' -o auto file1 file2 \
  | join -a1 -a2 -e '0' -o auto - file3   \
  | join -a1 -a2 -e '0' -o auto - file4   \
  | join -a1 -a2 -e '0' -o auto - file5

选项 -a1-a2 告诉 join 插入缺失的字段。 -e '0' 告诉它用零替换它们。输出由 -o auto 指定,它假定采用所有字段。

当有大量文件时,不能使用管道结构,但可以使用简单的 for 循环:

out=output
tmp=$(mktemp)
[[ -e "$out" ]] && rm -rf "$out" || touch "$out"
for file in f*; do
    join -a1 -a2 -e0 -o auto "$out" "$file" > "$tmp"
    mv "$tmp" "$out"
done
cat "$out"

或者如果你真的喜欢管道:

pipeline="cat /dev/null"
for file in f*; do pipeline="$pipeline | join -a1 -a2 -e0 -o auto - $file"; done
eval "$pipeline"

这里非常有趣: Is there a limit on how many pipes I can use?


备注: auto 的用法在这种情况下非常有用,但不是 POSIX standard 的一部分.它是一个 GNU 扩展,是 GNU coreutils 的一部分。 .纯 POSIX 版本会读起来有点麻烦:

$ join -a1 -a2 -e '0' -o 0 1.2 2.2 file1 file2 \
  | join -a1 -a2 -e '0' -o 0 1.2 1.3 2.2 - file3 \
  | join -a1 -a2 -e '0' -o 0 1.2 1.3 1.4 2.2 - file4 \
  | join -a1 -a2 -e '0' -o 0 1.2 1.3 1.4 1.5 2.2 - file5

关于 man join 的更多信息

关于bash - 根据匹配列合并多个文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55919150/

相关文章:

Bash:替换管道标准输入中的子字符串

windows - 更改 Emacs M-x shell 以使用 Windows 10 bash

mysql - Bash while 循环等待 mysql 导入

linux - 使用 jq 从 json 输出中获取键值

linux - 将文本附加到文件中仅第一列的末尾

linux - awk 中有多个条件

bash - 在 bash 中对目录进行排序

bash - 使用 sed/awk 插入文本中线

awk - 使用 awk 检查字段长度

linux - 我想将输出通过管道传输到 bash 中的多个文件