我有多个文件,需要根据每个文件的第一列进行合并
文件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
使用join
onFile1
和File2
将输出重定向到临时文件:
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/'
此版本还生成表的头行,其中所有文件名作为列标题。