我有两个大文件(27k 行和 450k 行)。它们看起来有点像:
File1:
1 2 A 5
3 2 B 7
6 3 C 8
...
File2:
4 2 C 5
7 2 B 7
6 8 B 8
7 7 F 9
...
我想要两个文件中第三列在两个文件中的行(注意带有 A 和 F 的行被排除在外):
OUTPUT:
3 2 B 7
6 3 C 8
4 2 C 5
7 2 B 7
6 8 B 8
什么是最好的方法?
我有两个大文件(27k 行和 450k 行)。它们看起来有点像:
File1:
1 2 A 5
3 2 B 7
6 3 C 8
...
File2:
4 2 C 5
7 2 B 7
6 8 B 8
7 7 F 9
...
我想要两个文件中第三列在两个文件中的行(注意带有 A 和 F 的行被排除在外):
OUTPUT:
3 2 B 7
6 3 C 8
4 2 C 5
7 2 B 7
6 8 B 8
什么是最好的方法?
首先我们对第三个字段的文件进行排序:
sort -k 3 file1 > file1.sorted
sort -k 3 file2 > file2.sorted
然后我们使用 comm 在第三个字段上获得公共值:
comm -12 <(cut -d " " -f 3 file1.sorted | uniq) <(cut -d " " -f 3 file2.sorted | uniq) > common_values.field
现在我们可以在公共值上加入每个排序的文件:
join -1 3 -o '1.1,1.2,1.3,1.4' file1.sorted common_values.field > file.joined
join -1 3 -o '1.1,1.2,1.3,1.4' file2.sorted common_values.field >> file.joined
输出已格式化,因此我们得到与文件中使用的字段顺序相同的字段顺序。使用的标准 unix 工具:sort、comm、cut、uniq、join。适用于 bash,对于<( )
其他 shell,您可以使用临时文件。
这是一个使用 grep、sed 和 cut 的选项。
提取第 3 列:
cut -d' ' -f3 file1 > f1c
cut -d' ' -f3 file2 > f2c
在 中查找匹配行file1
:
grep -nFf f2c f1c | cut -d: -f1 | sed 's/$/p/' | sed -n -f - file1 > out
在 中查找匹配行file2
:
grep -nFf f1c f2c | cut -d: -f1 | sed 's/$/p/' | sed -n -f - file2 >> out
输出:
3 2 B 7
6 3 C 8
4 2 C 5
7 2 B 7
6 8 B 8
如果您有非对称数据文件并且较小的数据文件适合内存,那么这种一次性 awk 解决方案将非常有效:
解析.awk
FNR == NR {
a[$3] = $0
p[$3] = 1
next
}
a[$3]
p[$3] {
print a[$3]
delete p[$3]
}
像这样运行它:
awk -f parse.awk file1 file2
file1
两者中较小的在哪里。
解释
FNR == NR
块读file1
入两个散列。a[$3]
如果是 中的键,则打印file2
行。$3
a
p[$3]
如果是一个键,则打印file1
行并删除该键(仅打印一次)。$3
p
awk '{print $3}' file1 | sort | uniq > file1col3
awk '{print $3}' file2 | sort | uniq > file2col3
grep -Fx -f file1col3 file2col3 | awk '{print "\\w+ \\w+ " $1 " \\w+"}' > col3regexp
egrep -xh -f col3regexp file1 file2
抓取两个文件中所有唯一的第 3 列,将它们相交(使用grep -F
),打印一堆将匹配您想要的列的正则表达式,然后使用egrep
从两个文件中提取它们。
首先从第三列获取公共值。然后过滤两个文件中具有匹配第三列的行。
如果列由单个字符分隔,您可以使用cut
提取一列。对于可以由任意数量的空格分隔的列,请使用awk
. 获取公共第 3 列值的一种方法是提取列,对它们进行排序并调用comm
. 使用 bash/ksh/zsh 进程替换:
comm -12 <(awk '{print $3}' file1 | sort -u) <(awk '{print $3}' file2 | sort -u)
现在把这些变成grep
模式,然后过滤。
comm -12 <(awk '{print $3}' file1 | sort -u) <(awk '{print $3}' file2 | sort -u) |
sed -e 's/[][.\|?*+^$]/\\&/g' \
-e 's/.*/^[^[:space]]+[[:space]]+[^[:space]]+[[:space]]+\1[[:space]]/' |
grep -E -f - file1 file2
上面的方法应该适用于大文件。但是在 500k 行时,您没有大文件。这些文件应该很适合内存,一个简单的 Perl 解决方案就可以了。加载两个文件,提取列值,打印匹配的列。
perl -n -e '
@lines += 1;
$c = (split)[2];
$seen{$c}{$ARGV} = 1;
END {
foreach (@lines) {
$c = (split)[2];
print if %{$seen{$c}} == 2;
}
}' file1 file2