可以有n列,但列数和标题名称在所有3个文件中保持相同。文件是制表符分隔符。
A.TXT
Name 9/1 9/2
X 1 7
y 2 8
z 3 9
a 4 10
b 5 11
c 6 12
b.xt
Name 9/1 9/2
X 13 19
y 14 20
z 15 21
a 16 22
b 17 23
c 18 24
c.txt
Name 9/1 9/2
X 25 31
y 26 32
z 27 33
a 28 34
b 29 35
c 30 36
必需的输出
Name 9/1 9/2
X 1/13/25 7/19/31
y 2/14/26 8/20/32
z 3/15/27 9/21/33
a 4/16/28 10/22/34
b 5/17/29 11/23/35
c 6/16/30 12/24/36
我想在匹配标题的基础上合并所有三个文件,如果行中的值相同,则打印一个值 如果不同,则连接该列下所有三个文件的值。比如文件a.txt,b.txt,c.txt, 第2行在列名“NAME”下具有相同的值,因此仅打印X但列“9/1”具有不同的值,因此打印一个标题“9/1”并在其下打印来自三个文件的所有值,例如25年1月13日
尝试下面
join <(sort a.txt) <(sort b.txt) <(sort c.txt) >out.txt
但它正在打印文件中的所有行a然后是起始文件b然后是c 同样的事情
awk 'FNR==NR{a[$1]=$2;next} ($1 in a){ print $0, a[$1]}' a.txt b.txt c.txt
答案 0 :(得分:2)
输出顺序是Gnu awk默认值:
$ cat > combine.txt
FNR==NR {
a[$1]=$2 # gather the 2 dates in variables
b[$1]=$3
next
}
FNR>1 && ($1 in a) {
a[$1]=a[$1] "/" $2
b[$1]=b[$1] "/" $3
}
END { # print built up variables in the end
# PROCINFO["sorted_in"]="@ind_str_asc" # output order control with this
# in Gnu awk
for(i in a) print i, a[i], b[i]
}
测试它:
$ awk -f comnine.awk a.txt b.txt c.txt
y 2/14/26 8/20/32
z 3/15/27 9/21/33
a 4/16/28 10/22/34
b 5/17/29 11/23/35
c 6/18/30 12/24/36
X 1/13/25 7/19/31
Name 9/1 9/2
Gnu awk中的修订版本应该支持超过2个数据字段。我只测试了您提供的数据。让我知道它是否有效,我删除了原有的限制版本。
FNR==NR {
for(i=2;i<=NF;i++)
a[$1][i]=$i
next
}
FNR>1 && ($1 in a) {
for(i=2;i<=NF;i++)
a[$1][i]=a[$1][i] "/" $i
}
END { # print built up variables in the end
# PROCINFO["sorted_in"]="@ind_str_asc" # output order control with this
# in Gnu awk
for(i in a) {
printf "%s", i OFS
for(j=2;j<=NF;j++)
printf "%s", a[i][j] (j<NF?OFS:ORS)
}
}
由于你有tagget join
,我还是决定尝试一下:
$ join -o 1.1 1.2 2.2 2.4 1.3 2.3 2.5 a.txt <(join b.txt c.txt) | sed 's/ /\//g2; s/\// /3'
Name 9/1/9 1/9/1/9/2/9/2/9/2
X 1/13/25 7/19/31
y 2/14/26 8/20/32
z 3/15/27 9/21/33
a 4/16/28 10/22/34
b 5/17/29 11/23/35
c 6/18/30 12/24/36
即。首先加入b.txt
和c.txt
,然后将结果加入a.txt
。使用sed
替换某些空格时,/
用于输出控制。 man join
显示--header
转换,但它不想与我合作。
答案 1 :(得分:1)
这一次从所有文件中读取一行,并为每个文件和列连接相同的字段。假设所有文件中每行的第一个字段相同。 (提交已排序的文件。)
use warnings 'all';
use strict;
# Implicit filehandles. Use readline() to read from them, not <>
my @fh = map { open my $fh, '<', $_ or die "Can't open $_: $!"; $fh } @ARGV;
while (1)
{
# Read a line from all files. Exit loop if any is undefined
my @line = map { scalar readline $_ } @fh;
last if grep { not defined $line[$_] } 0..$#line;
# Print first line (header) from one file, skip further processing
if ($. == 1) {
print $line[0];
next;
}
# Get the first column from one file, assumed the same for all
my @out_line = (split ' ', $line[0])[0];
# Join same column from all files with '/', for all columns
for my $i (1..$#line) {
push @out_line, join '/', map { (split)[$i] } @line;
}
print "@out_line\n";
}
在给定排序的输入文件的情况下,使用script.pl a.txt b.txt c.txt
生成所需的输出。
评论。如果更全面的解释会有所帮助,请告诉我。
假设第一列相同。一个文件耗尽后立即退出
使用“隐式文件句柄” - 通过readline
而不是<>
运算符从中读取。请参阅in perlfaq5
scalar readline $_
强制标量上下文,以便读取一行
留下换行符。要删除它,请在chomp(@lines)
last if ...
前两行可用于构成while (...)
条件
原帖,浓缩。留在这里是为了讨论join
这使您几乎可以使用join
,并使用Perl完成工作。
首先,join
当时只占用两个文件。因此,您可以使用前两个运行它,然后使用生成的文件和最后一个文件运行它。我们还需要告诉它如何格式化输出。这是通过其-o
选项完成的,
这允许我们将输出行的元素列为FileN.fieldN, ...
您需要的是:1.1
1.2
2.2
1.3
2.3
- 文件1中的字段1,2,然后是2中的2,等等< / p>
join -o 1.1,1.2,2.2,1.3,2.3 a.txt b.txt > ab.txt
文件ab.txt
具有不同的格式,因此行的-o
格式会在下一步中发生变化
join -o 1.1,1.2,1.3,2.2,1.4,1.5,2.3 ab.txt c.txt > abc.txt
这有两个故障 - 标题已连接,字段与空格合并。用Perl单行修复。
收集bash脚本。工作草图,以script.sh a.txt b.txt c.txt
#!/bin/bash
join -o 1.1,1.2,2.2,1.3,2.3 $1 $2 > tmp.$$
join -o 1.1,1.2,1.3,2.2,1.4,1.5,2.3 tmp.$$ $3 > abc.txt
# Remove extra fields in the header
perl -i -wpe '$.==1 && s{(\d/\d) \1 \1}{$1}g' abc.txt
# Replace space with `/` between every three numbers
perl -i -wpe 's{(\d+) (\d+) (\d+)}{$1/$2/$3}g' abc.txt
rm -f tmp.$$
请更仔细地选择临时名称并添加错误检查。输出abc.txt
Name 9/1 9/2 X 1/13/25 7/19/31 y 2/14/26 8/20/32 ...
答案 2 :(得分:1)
这可能适合你(GNU sed&amp; bash):
对于给出的示例(两列):
sed -sr '1d;s#(.*\t)(.*)\t(.*)#/^\1/s@(\t.*)(\t.*)@\\1/\2\\2/\3@#' file{b,c} |
sed -rf - filea
这将从第一个以外的文件构建一个sed脚本,该脚本插入与每行的键匹配的值。除了在构建脚本时以正确的顺序放置文件之外,不需要排序。然后管道创建的脚本并针对第一个文件运行以获得所需的结果。
N.B。 -s
选项允许每个文件的标题行与每个子文件无关,父文件将不匹配,并且将保持不变。
然而,问题允许使用数量不详的列,这需要一个类似但更复杂的解决方案:
sed -sr '1d;s#[^\t]+#/^&/{#;s#\t([^\t]+)#s@\\t([^\\t]+)@\\n\\1/\1@;#g;s#$#y/\\n/\\t/;}#' file{b,c}|
sed -rf - filea
在此解决方案中,父/子文件中的每个选项卡都需要替换命令(如前面的解决方案中,后向引用已命名,因此限制为9)。通过删除选项卡并将其替换为换行符来关闭每个替换,从而将后向引用减少为仅一个,然后通过再次将换行符替换为制表符来替换换行符。