bash:清理三个文件的外部连接,保留文件成员资格

标签 bash awk outer-join

考虑以下三个标题位于第一行的文件:

文件1:

id name in1
1 jon 1
2 sue 1

文件2:

id name in2
2 sue 1
3 bob 1

文件3:

id name in3
2 sue 1
3 adam 1

我想合并这些文件以获得以下输出,merged_files:

id name in1 in2 in3
1 jon 1 0 0
2 sue 1 1 1
3 bob 0 1 0
3 adam 0 0 1

这个请求有几个我没有发现在 grep/sed/awk/join 等中以方便的方式实现的特殊功能。编辑:为简单起见,您可以假设这三个文件已经排序。

最佳答案

这与 Bash script to find matching rows from multiple CSV files 中解决的问题非常相似.它不完全相同,但非常相似。 (非常相似,我只需要删除三个 sort 命令,稍微更改三个 sed 命令,更改文件名,将“缺失”值从 no 更改为 0 ,并更改替换最后 sed 从逗号到空格。)

join命令 sed (通常也是 sort,但数据已经充分排序)是所需的主要工具。假设:没有出现在原始数据中。要记录文件中一行的存在,我们需要 1文件中的字段(几乎就在那里);我们会有 join供应0当没有比赛时。 1在每个非标题行的末尾需要变成 :1 , 并且标题中的最后一个字段也需要以 : 开头.然后,使用 bashprocess substitution ,我们可以写:

$ sed 's/[ ]\([^ ]*\)$/:\1/' file1 |
> join -t: -a 1 -a 2 -e 0 -o 0,1.2,2.2     - <(sed 's/[ ]\([^ ]*\)$/:\1/' file2) |
> join -t: -a 1 -a 2 -e 0 -o 0,1.2,1.3,2.2 - <(sed 's/[ ]\([^ ]*\)$/:\1/' file3) |
> sed 's/:/ /g'
id name in1 in2 in3
1 jon 1 0 0
2 sue 1 1 1
3 adam 0 0 1
3 bob 0 1 0
$

sed命令(三次)添加 :在文件每一行的最后一个字段之前。连接几乎是对称的。 -t:指定字段分隔符为冒号; -a 1-a 2意味着当文件中没有匹配项时,该行仍将包含在输出中; -e 0意味着如果文件中没有匹配项,则 0在输出中生成;和 -o选项指定输出列。对于第一次加入,-o 0,1.2,2.2输出是连接列 (0),然后是两个文件中的第二列(1)。第二个连接在输入中有 3 列,因此它指定 -o 0,1.2,1.3,2.2 .参数-就其本身而言意味着“读取标准输入”。 <(...)表示法是“进程替换”,其中文件名(通常为 /dev/fd/NN )提供给连接命令,它包含括号内命令的输出。然后通过 sed 过滤输出再次用空格替换冒号,产生所需的输出。

与所需输出的唯一区别是 3 bob 的排序在 3 adam 之后;您在所需输出中反向订购它们的依据不是特别清楚。如果它很重要,可以设计一种方法来以不同方式解决顺序(例如 sort -k1,1 -k3,5 ,除了在数据之后对标签行进行排序;如有必要,有解决方法)。

关于bash:清理三个文件的外部连接,保留文件成员资格,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17507765/

相关文章:

bash - 使用 mysql 的结果填充 zenity 列表

bash - 将状态(文件完整性)检查添加到 cbr cbz 转换 bash 脚本

oracle - ORA 00904 带有左/右外连接的标识符无效

mysql - LEFT OUTER JOIN,按日期过滤

python - pandas - 使用外部连接扩展 DataFrame

Bash:值对于基础来说太大了(带数字的字符串)

linux - Bash 命令在命令行中的服务器中执行多项操作

unix - AWK : Field Separator as Double Pipes or more

linux - 使用 awk 查找两个以上文件中共有的行

regex - sed:将部分输出复制到表中