将基于第一列的多个文件粘贴到一个文件中

将基于第一列的多个文件粘贴到一个文件中

我有多个文件,需要根据每个文件的第一列进行合并

文件1:

foo  12  
jhdfeg 25  
kjfdgkl 37

文件2:

foo 23  
jhdfeg 45

文件3:

foo 35  
djhf 37  

输出应该是这样的

        file1 file2  file3  
foo     12    23     35  
jhdfeg  25    45     0  
kjfdgkl 37    0      0  
djhf    0     0      37

答案1

awk方法:

joiner.awk脚本:

#!/bin/awk -f
BEGIN { 
    f1=ARGV[1]; f2=ARGV[2]; f3=ARGV[3]     # the 1st, 2nd and 3rd file names respectively
    printf("%10s\t%s\t%s\t%s\n", "", f1, f2, f3)   # printing header
}
{ a[$1][FILENAME]=$2 }    # accumulating values
END {
    for (i in a) {
        printf("%-10s\t%d\t%d\t%d\n", i, a[i][f1], a[i][f2], a[i][f3]) 
    }
}

用法:

awk -f joiner.awk file1 file2 file3

输出:

          file1 fil2 file3
kjfdgkl     37  0   0
foo         12  23  35
djhf        0   0   37
jhdfeg      25  45  0

答案2

perl -F'\s+' -lane '

   $. == 1 and @ARGC = ($ARGV, @ARGV);       # initialize the @ARGC array

   exists $h{$F[0]} or $h[keys %h] = $F[0];  # needed to remember order

   $h{$F[0]}->[@ARGC-@ARGV-1] = $F[1];       # populate hash

   END {
      $, = "\t";           # set the OFS to TAB

      print q//, @ARGC;    # print the first line with filenames

      for my $key (@h) {   # print remaninig lines with data
         print $key,
            map { $h{$key}->[$_] // 0 } 0 .. $#ARGC;
      }
   }

' file1 file2 file3 # ... you can give as many files here

输出

        file1   file2   file3
foo     12      23      35
jhdfeg  25      45      0
kjfdgkl 37      0       0
djhf    0       0       37

答案3

如果你有 3 个文件,就像你的例子一样,你可以用一些 join魔法来做到这一点。首先,将制表符分隔的文件名称写入输出文件:

for i in File*; do printf "\t%s" "$i" >> RES; done

为实际结果添加一个空行:

printf '\n' >> RES

使用joinonFile1File2将输出重定向到临时文件:

join  -a1 -a2  -e0 <(sort File1) -o 0 1.2 2.2 <(sort File2) > TEMP_FILE

现在再次使用上面命令的输出File3(您也可以在此处使用管道 ( |)):

join  -a1 -a2  -e0 <(sort TEMP_FILE) -o 0 1.2 1.3 2.2 <(sort File3) >> RES

并将空格替换为制表符RES

tr ' ' '\t' < RES > FINAL_RES

您的结果位于FINAL_RES

$ cat FINAL_RES
        File1   File2   File3
foo     12      23      35
jhdfeg  25      45      0
kjfdgkl 37      0       0

答案4

这里有一个更通用的方法,与文件的数量无关sed

sed '1{x;s/$/_/;x;}
  /foo/{x;s/_/ 0_/g;x;}
  G;s/^\([a-z]*\)  *\([0-9]*\).*\n\(.*_\)\1\([^_]*\)0/\3\1\4\2/
  s/^\([a-z]*\) *\([0-9]*\).*\n\([^_]*\)0_\(.*\)/\30_\4\1\3\2_/
  $! {h;d;}
  s/[^_]*_//
  y/_/\n/' file*

这依赖于以该foo行开头的每个文件,如您的示例所示。

鉴于您已经了解sed工作原理、模式空间和保持空间的基本知识,以下是解释:

主要思想是在保留空间中构建整个输出表。每行的保留空间都包含该点的表格以及新行所需的模板行。我们_在处理过程中用作行分隔符。现在一步一步:

1{x;s/$/_/;x;}

这将使用单个_作为模板行的开头来初始化保留空间。

/foo/{x;s/_/ 0_/g;x;}

/foo/寻址包含 的行foo,这表示新文件的开始。在这种情况下,将执行 中的命令{}: 保留空间中的每一行(实际表行和模板行)都会被0追加。如果我们稍后遇到该行的关键字,则将0被替换为正确的数字;如果该关键字没有出现,则0保留。

G;s/^\([a-z]*\) *\([0-9]*\).*\n\(.*_\)\1\([^_]*\)0/\3\1\4\2/

'G' 将保留空间附加到模式空间。该s命令有四个\(\)部分:第一部分包含关键字,第二部分包含值,第三部分包含换行符之后的所有内容(因此这是从保留空间附加的表),直到第二次出现关键字(反向引用\1)。第四个拥有该行中除最后一个之外的所有内容0。因此,我们找到了带有该关键字的现有行并将0.我们将所有内容都放到换行符处,只保留更新后的表。

s/^\([a-z]*\) *\([0-9]*\).*\n\([^_]*\)0_\(.*\)/\30_\4\1\3\2_/

另一个匹配包含换行符\n,所以我们知道我们没有在表中找到关键字(否则换行符会在之前的行中被删除。所以这次我们在末尾添加一个新行,由关键字组成,模板行和值。这就是模板行的技巧:我们0为每个新文件添加了一列,因此如果我们删除一列,则每个不存在该关键字的文件0都会有一列。0

$! {h;d;}

如果这不是最后一行,请将修改后的表移回保留空间 ( h) 并重新开始 ( d)。

s/[^_]*_//

对于最后一行,这将删除模板行。

y/_/\n/

这将替换_为换行符。此外,如果您愿意,还可以用制表符替换空格。

编辑

如果每个文件都以该行开头的假设是错误的foo,我们需要一种不同的方法来判断sed新文件何时开始,例如为每个文件开头添加额外的行并将整个内容流式传输到sed

for file in file*; do
  echo Start of $file
  cat $file
done | sed '1{x;s/$/__/;x;}
  /Start of/{G;s/_/ 0_/g;s/Start of \(.*\)\n\([^_]*\)_\([^_]*\) 0/\2_\3 \1/;x;d;}
  G;l;s/^\([a-z]*\)  *\([0-9]*\).*\n\(.*_\)\1\([^_]*\)0/\3\1\4\2/
  l;s/^\([a-z]*\) *\([0-9]*\).*\n\([^_]*\)0_\(.*\)/\3 0_\4\1\3\2_/
  $! {h;d;}
  s/[^_]*_//
  y/_/\n/'

此版本还生成表的头行,其中所有文件名作为列标题。

相关内容