从包含数字列的两个文件中打印匹配行

从包含数字列的两个文件中打印匹配行

我一直在尝试做以下事情:

  • 我有一个包含 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

相关内容