我一直在尝试做以下事情:
- 我有一个包含 3 列数字的文件,与包含一列数字的较小文件相比,我一直想打印匹配的行。
- 我正在使用,
grep -F
但它总是给我不匹配的行。
答案1
我的猜测是,101
当较小的文件包含10
在其中时,您会看到诸如匹配之类的内容。这是因为 10 是 101 的子串。
如果你的主文件只有一个字段,你可以使用 grep 的-x
选项来精确匹配。例如
grep -x -F -f smallerfile.txt mainfile.txt
因为它有三个字段,所以您必须单独匹配每个字段,因此您必须使用 perl 或 awk 之类的东西。例如,使用 awk:
$ cat smallerfile.txt
1
10
25
152
$ cat mainfile.txt
1 2 3
5 10 15
10 11 12
100 101 102
150 151 152
250 255 260
$ awk 'FNR == NR { nums[$1]++ ; next }
$1 in nums || $2 in nums || $3 in nums' smallerfile.txt mainfile.txt
1 2 3
5 10 15
10 11 12
150 151 152
顺便说一句,如果您必须检查三个以上的字段,那么编写或更新脚本会变得很乏味。为了避免这种情况,您可以编写一个 awk 函数来迭代每个字段(无论有多少个字段)并检查其中任何一个字段的值是否在数组中nums
。例如
$ awk 'function check_all_fields() {
for (i=1; i<=NF; i++) {
if ($i in nums) return 1
}
return 0
}
FNR == NR { nums[$1]++ ; next }
check_all_fields()' smallerfile.txt mainfile.txt
1 2 3
5 10 15
10 11 12
150 151 152
如您所见,输出与第一个版本相同。
check_all_fields()
一旦函数在输入行中看到匹配的字段,就会返回 1 (true) 。如果在当前行中从未看到 1,则返回 0 (false)。
答案2
我们可以使用以下方法来做到这一点,其中我们利用它们的邻域来sed
指导grep
搜索字符串:
sed -e 's/.*/\\<&\\>/' f1 | grep -Ef - f3col
输出:-
1 2 3
5 10 15
10 11 12
150 151 152
使用awk
我们可以如图所示进行操作。我们首先形成一个a[...]
以单列文件的列为键的关联数组。之后,在三列文件的每一行上,我们初始化标志p
并在单列文件中存在与任何匹配的数据时递增它字段。然后,在 for 循环结束时,如果该标志至少增加过一次,我们有条件地打印记录。
awk '
NR==FNR{a[$1];next}
{
for (p=i=1; i<=NF; i++) if ($i in a) p++
}p>1
' f1 f3col
python
本身支持set
我们执行的数据结构,如下所示,当集合的交集非空时进行打印:
python3 -c 'import sys
file1,file2 = sys.argv[1:]
with open(file1) as f1, open(file2) as f2:
s1 = { _.rstrip() for _ in f1 }
for _ in f2:
s2 = set(_.rstrip().split())
if bool(s2 & s1):
print(_,end="")
' f1 f3col
使用POSIX sed
构造,我们首先使用单列文件生成 POSIX sed 代码,然后将其应用于三列文件:
sed -e '
h;G;G
s/.*/^&$/
s|\n| /b&/ |g
s|.*|/&/b|;$a\
d
' f1 | sed -f - f3col
perl
可以通过多种方式做到这一点:
perl -lane '
@A || chomp(@A=<STDIN>);
for my $f (@F) {
print,last if grep { $f == $_ } @A;
}
' f3col < f1
perl -MList::Util=any -lane '
chomp(@A=<STDIN>) if !@A;
print if
any {
my $f = $_;
any { $_ == $f } @A;
} @F;
' f3col < f1