在Unix上连接文本文件中的多个字段

nev*_*int 13 unix linux bash join

我该怎么做?

File1看起来像这样:

foo 1 scaf 3 
bar 2 scaf 3.3
Run Code Online (Sandbox Code Playgroud)

File2看起来像这样:

foo 1 scaf 4.5
foo 1 boo 2.3
bar 2 scaf 1.00
Run Code Online (Sandbox Code Playgroud)

我想要做的是找到 在字段1,2和3相同时在File1File2中共同出现的行.

有办法吗?

the*_*edk 14

这是正确的答案(在使用标准GNU coreutils工具方面,而不是在perl/awk中编写自定义脚本).

$ join -j1 -o1.2,1.3,1.4,1.5,2.5 <(<file1 awk '{print $1"-"$2"-"$3" "$0}' | sort -k1,1) <(<file2 awk '{print $1"-"$2"-"$3" "$0}' | sort -k1,1)
bar 2 scaf 3.3 1.00
foo 1 scaf 3 4.5
Run Code Online (Sandbox Code Playgroud)

好的,它是如何工作的:

  1. 首先,我们将使用一个join可以合并两条线的好工具.join有两个要求:

    • 我们只能通过一个字段加入.
    • 两个文件必须按键列排序!
  2. 我们需要在输入文件中生成密钥,为此我们使用一个简单的awk脚本:

    $ cat file1
    foo 1 scaf 3
    bar 2 scaf 3.3    
    
    $ <file1 awk '{print $1"-"$2"-"$3" "$0}'
    foo-1-scaf foo 1 scaf 3
    bar-2-scaf bar 2 scaf 3.3
    
    Run Code Online (Sandbox Code Playgroud)

    你看,我们在第一列添加了一些像" foo-1-scaf " 这样的关键字.我们对file2做同样的事情.BTW.<file awk,只是花哨的写作方式awk file,或cat file | awk.

    我们还应该按键对文件进行排序,在我们的例子中这是第1列,所以我们添加到命令的末尾| sort -k1,1(从第1列到第1列的文本排序)

  3. 此时我们可以生成文件file1.with.keyfile2.with.key并加入它们,但假设这些文件很大,我们不想将它们复制到文件系统上.相反,我们可以使用一个称为bash 进程替换的东西来生成命名管道的输出(这将避免任何不必要的中间文件创建).有关详细信息,请阅读提供的链接.

    我们的目标语法是: join <( some command ) <(some other command)

  4. 最后一件事是解释花哨的连接参数: -j1 -o1.2,1.3,1.4,1.5,2.5

    • -j1 - 按第1列中的键加入(在两个文件中)
    • -o - 仅输出那些字段1.2(第1.3一个文件字段2),(第一个文件列3)等.

      这样我们就加入了行,但join只输出必要的列.

从这篇文章中吸取的教训应该是:

  • 你应该掌握coreutils包,这些工具组合起来非常强大,你几乎不需要编写自定义程序来处理这种情况,
  • core utils工具也非常快速且经过严格测试,因此它们始终是最佳选择.


Jon*_*ler 11

join命令很难使用,只能连接一列

大量的实验加上对手册页的仔细审查表明你不能直接加入多个列 - 而且我所有的加入实例都很有趣,只使用一个连接列.

因此,任何解决方案都需要以某种方式将要连接的列连接成一列.标准连接命令还要求其输入处于正确的排序顺序 - 在GNU连接(info coreutils join)中有一个关于它并不总是需要排序数据的注释:

但是,作为GNU扩展,如果输入没有不可修改的行,则排序顺序可以是任何将两个字段视为相等的顺序,当且仅当上述排序比较认为它们相等时.

使用给定文件执行此操作的一种可能方法是:

awk '{printf("%s:%s:%s %s %s %s %s\n", $1, $2, $3, $1, $2, $3, $4);}' file1 |
sort > sort1
awk '{printf("%s:%s:%s %s %s %s %s\n", $1, $2, $3, $1, $2, $3, $4);}' file2 |
sort > sort2
join -1 1 -2 1 -o 1.2,1.3,1.4,1.5,2.5 sort1 sort2
Run Code Online (Sandbox Code Playgroud)

这将在开始时创建一个复合排序字段,使用":"分隔子字段,然后对文件进行排序 - 对于两个文件中的每一个.然后,join命令将连接两个复合字段,但仅打印出非复合(非连接)字段.

输出是:

bar 2 scaf 3.3 1.00
foo 1 scaf 3 4.5
Run Code Online (Sandbox Code Playgroud)

尝试进行连接的失败会执行不会执行的操作

join -1 1 -2 1 -1 2 -2 2 -1 3 -2 3 -o 1.1,1.2,1.3,1.4,2.4 file1 file2

在MacOS X 10.6.3上,这给出了:

$ cat file1
foo 1 scaf 3 
bar 2 scaf 3.3
$ cat file2
foo 1 scaf 4.5
foo 1 boo 2.3
bar 2 scaf 1.00
$ join -1 1 -2 1 -1 2 -2 2 -1 3 -2 3 -o 1.1,1.2,1.3,1.4,2.4 file1 file2
foo 1 scaf 3 4.5 
bar 2 scaf 3.3 4.5 
$
Run Code Online (Sandbox Code Playgroud)

这是加入第3场(仅限) - 这不是想要的.

您需要确保输入文件的排序顺序正确.


Mic*_*zek 5

将前三个字段与awk结合起来可能是最简单的:

awk '{print $1 "_" $2 "_" $3 " " $4}' filename
Run Code Online (Sandbox Code Playgroud)

然后,您可以join在“字段1”上正常使用


gho*_*g74 4

你可以试试这个

awk '{
 o1=$1;o2=$2;o3=$3
 $1=$2=$3="";gsub(" +","")
 _[o1 FS o2 FS o3]=_[o1 FS o2 FS o3] FS $0
}
END{ for(i in _) print i,_[i] }' file1 file2
Run Code Online (Sandbox Code Playgroud)

输出

$ ./shell.sh
foo 1 scaf  3 4.5
bar 2 scaf  3.3 1.00
foo 1 boo  2.3
Run Code Online (Sandbox Code Playgroud)

如果你想省略不常见的行

awk 'FNR==NR{
 s=""
 for(i=4;i<=NF;i++){ s=s FS $i }
 _[$1$2$3] = s
 next
}
{
  printf $1 FS $2 FS $3 FS
  for(o=4;o<NF;o++){
   printf $i" "
  }
  printf $NF FS _[$1$2$3]"\n"
 } ' file2 file1
Run Code Online (Sandbox Code Playgroud)

输出

$ ./shell.sh
foo 1 scaf 3  4.5
bar 2 scaf 3.3  1.00
Run Code Online (Sandbox Code Playgroud)