代码之家  ›  专栏  ›  技术社区  ›  Zsolt Botykai

根据当前文件的内容从awk中搜索/读取另一个文件,是否可能?

  •  2
  • Zsolt Botykai  · 技术社区  · 17 年前

    我正在用(GNU)处理一个大文件 awk ,(其他可用的工具有:LinuxShell工具,一些旧的(>5.0)版本的Perl,但无法安装模块)。

    我的问题:如果某个field1、field2、field3包含x、y、z,我必须在另一个目录中搜索一个文件,其中一行包含field4和field5,并将找到的文件中的一些数据插入当前输出。

    例如。:

    实际文件行:

    f1 f2 f3 f4 f5
    X  Y  Z  A  B
    

    现在我需要搜索另一个文件(在另一个目录中),其中包含

    f1 f2 f3 f4
    A  U  B  W
    

    写到stdout $0 从原始文件,以及 f2 f3 从找到的文件,然后处理原始文件的下一行。

    可以用它吗 AWK ?

    4 回复  |  直到 10 年前
        1
  •  2
  •   tsee    17 年前

    首先,我要说的是,你的问题描述并没有那么有帮助。下次,请更具体一点:你可能会错过更好的解决方案。

    所以从您的描述中,我了解到您有两个包含空格分隔数据的文件。在第一个文件中,您希望根据某些搜索模式匹配前三列。如果找到,您希望在另一个文件中查找包含第一个文件中匹配行的第四列和第五列的所有行。从这些行中,您需要提取第二列和第三列,然后打印第一个文件的第一列和第二个文件的第二列和第三列。好的,这里是:

    #!/usr/bin/env perl -nwa
    use strict;
    use File::Find 'find';
    my @search = qw(X Y Z);
    
    # if you know in advance that the otherfile isn't
    # huge, you can cache it in memory as an optimization.
    
    # with any more columns, you want a loop here:
    if ($F[0] eq $search[0]
        and $F[1] eq $search[1]
        and $F[2] eq $search[2])
    {
      my @files;
      find(sub {
          return if not -f $_;
          # verbatim search for the columns in the file name.
          # I'm still not sure what your file-search criteria are, though.
          push @files, $File::Find::name if /\Q$F[3]\E/ and /\Q$F[4]\E/;
          # alternatively search for the combination:
          #push @files, $File::Find::name if /\Q$F[3]\E.*\Q$F[4]\E/;
          # or search *all* files in the search path?
          #push @files, $File::Find::name;
        }, '/search/path'
      )
      foreach my $file (@files) {
        open my $fh, '<', $file or die "Can't open file '$file': $!";
        while (defined($_ = <$fh>)) {
          chomp;
          # order of fields doesn't matter per your requirement.
          my @cols = split ' ', $_;
          my %seen = map {($_=>1)} @cols;
          if ($seen{$F[3]} and $seen{$F[4]}) {
            print join(' ', $F[0], @cols[1,2]), "\n";
          }
        }
        close $fh;
      }
    } # end if matching line
    

    与另一个包含大量系统调用的海报解决方案不同,这根本不会回到shell中,因此应该足够快。

        2
  •  1
  •   Tanktalus    17 年前

    这是一种让我从awk迁移到perl的工作。如果要完成这一点,您可能会发现创建一个shell脚本更容易,该脚本创建一个awk脚本来进行查询,然后在单独的步骤中进行更新。

    (我为阅读/更新windows ini风格的文件写了这样一个怪兽——真难看。我真希望我能用Perl。)

        3
  •  1
  •   Solyad    17 年前

    我经常看到这样的限制:“我不能使用任何Perl模块”,当它不是一个家庭作业问题时,通常是由于缺少信息。 Yes, even you can use CPAN 包含有关如何在没有根权限的情况下本地安装CPAN模块的说明。另一种选择是获取CPAN模块的源代码并将其粘贴到程序中。

    如果存在其他未声明的限制(如磁盘空间不足,无法安装(太多)附加文件),这些都没有帮助。

        4
  •  0
  •   Adam Bellaire    17 年前

    这似乎对我设置的一些与您的示例匹配的测试文件有效。以这种方式使用Perl(插入grep)可能会极大地损害性能,尽管…

    ## perl code to do some dirty work
    
    for my $line (`grep 'X Y Z' myhugefile`) {
        chomp $line;
        my ($a, $b, $c, $d, $e) = split(/ /,$line);
        my $cmd = 'grep -P "' . $d . ' .+? ' . $e .'" otherfile';
        for my $from_otherfile (`$cmd`) {
            chomp $from_otherfile;
            my ($oa, $ob, $oc, $od) = split(/ /,$from_otherfile);
            print "$a $ob $oc\n";
        }
    }
    

    编辑: 使用谢的解决方案(上图),它的思路更为周密。