【问题标题】:Searching/reading another file from awk based on current file's contents, is it possible?根据当前文件的内容从 awk 搜索/读取另一个文件,这可能吗?
【发布时间】:2010-09-13 17:54:05
【问题描述】:

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

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

例如:

实际文件行:

f1 f2 f3 f4 f5
X  Y  Z  A  B

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

f1 f2 f3 f4
A  U  B  W

并从原始文件写入STDOUT $0,从找到的文件写入f2f3,然后处理原始文件的下一行。

awk可以做到吗?

【问题讨论】:

  • 不确定我能否根据您的示例理解您要执行的操作-您能否澄清一下?您用来查找其他文件的标准是什么?为什么要从第二个文件中输出 f2 和 f3,或者这只是一个固定要求?

标签: perl search shell awk command-line-interface


【解决方案1】:

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

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

#!/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,因此应该非常快。

【讨论】:

  • 抱歉没有正确指定。我也会在工作中尝试您的解决方案。一个问题:如何解决,otherfile 的名称(您的答案中的 t.txt)未知:所以我需要搜索 fpr 一个符合我的条件的文件?
  • 文件名的标准是什么?你应该做的是:使用 File::Find。这是一个递归遍历目录的模块。它已在 perl 5.0 中,因此您可以安全地使用它。
  • 这是一个比我的 hack 更好的解决方案,它将两个 grep 的全部内容加载到内存中并且(可能)非常缓慢。很高兴看到添加 File::Find 以获得完整的解决方案。
【解决方案2】:

这是让我首先从 awk 转到 perl 的工作类型。如果您要完成此操作,您实际上可能会发现创建一个创建 awk 脚本以进行查询然后分步更新的 shell 脚本更容易。

(我写了这样一个用于读取/更新 windows-ini 样式文件的野兽——它很难看。我希望我可以使用 perl。)

【讨论】:

    【解决方案3】:

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

    如果存在其他未说明的限制,例如磁盘空间不足会阻止安装(太多)附加文件,则这些都无济于事。

    【讨论】:

    • 你几乎是对的,除了 - 非常严格的系统管理员 - 在一家非常大的银行 - 在一个实时系统上 - 我刚刚接到一个电话,要求我没有正确记录我对文件所做的事情- 我的 .vimrc - 没有任何互联网连接到机器 - 我必须要求管理员上传文件......
    【解决方案4】:

    这似乎适用于我设置的与您的示例匹配的一些测试文件。以这种方式(插入 grep)涉及 perl 可能会极大地损害性能,但是...

    ## 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";
        }
    }
    

    编辑:使用 tsee 的解决方案(上图),这是经过深思熟虑的。

    【讨论】:

    • 涉及 perl 完全不会影响性能!使用 perl 中的反引号调用 shell 命令(就像你一样)会破坏性能。如果您使用 shell 典型的习惯用法,即通过大量程序传递信息或调用许多额外的进程,那么您将降低性能。
    • 你说的很对,谢。我的意思是,以这种特殊方式涉及 perl 会损害性能。您正确编写的脚本要好得多。
    • 抱歉这么直率,我误解了你帖子的第二句话。干杯,
    猜你喜欢
    • 2017-03-05
    • 2013-09-01
    • 2013-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-09
    • 1970-01-01
    • 2011-09-21
    相关资源
    最近更新 更多