Возможен ли поиск/чтение другого файла из awk на основе содержимого текущего файла?

StackOverflow https://stackoverflow.com/questions/144321

Вопрос

Я обрабатываю огромный файл с помощью (GNU) awk, (другие доступные инструменты:Инструменты оболочки Linux, какая-то старая (>5.0) версия Perl, но модули не устанавливаются).

Моя проблема:если какое-то поле1, поле2, поле3 содержит X, Y, Z, я должен найти файл в другом каталоге, который содержит поле4 и поле5 в одной строке, и вставить некоторые данные из найденного файла в текущий вывод.

Например.:

Фактическая строка файла:

f1 f2 f3 f4 f5
X  Y  Z  A  B

Теперь мне нужно найти другой файл (в другом каталоге), который содержит, например.

f1 f2 f3 f4
A  U  B  W

И напишите в STDOUT $0 из исходного файла и f2 и f3 из найденного файла, затем обработайте следующую строку исходного файла.

Можно ли это сделать с awk?

Это было полезно?

Решение

Позвольте мне начать с того, что ваше описание проблемы на самом деле не так уж и полезно.В следующий раз, пожалуйста, будьте конкретнее:Возможно, вы упускаете гораздо лучшие решения.

Итак, из вашего описания я понимаю, что у вас есть два файла, которые содержат данные, разделенные пробелами.В первом файле вы хотите сопоставить первые три столбца с некоторым шаблоном поиска.Если они найдены, вы хотите найти все строки в другом файле, которые содержат четвертый и пятый столбцы соответствующей строки в первом файле.Из этих строк вам нужно извлечь второй и третий столбцы, а затем распечатать первый столбец первого файла, а также второй и третий столбцы из второго файла.Хорошо, вот так:

#!/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

В отличие от решения другого автора, которое содержит множество системных вызовов, оно вообще не обращается к оболочке и, следовательно, должно работать достаточно быстро.

Другие советы

Именно такая работа в первую очередь побудила меня перейти с awk на Perl.Если вы собираетесь это сделать, возможно, вам будет проще создать сценарий оболочки, который создает сценарии awk для запроса и последующего обновления в виде отдельных шагов.

(Я написал вот такую ​​зверюгу для чтения/обновления файлов в стиле windows-ini — она уродлива.Мне бы хотелось использовать Perl.)

Я часто вижу ограничение «Я не могу использовать какие-либо модули Perl», а если это не вопрос домашнего задания, то зачастую это происходит просто из-за недостатка информации. Да, даже вы можете использовать CPAN содержит инструкции по локальной установке модулей CPAN без прав root.Другая альтернатива — просто взять исходный код модуля CPAN и вставить его в свою программу.

Ничто из этого не помогает, если существуют другие, неуказанные ограничения, такие как нехватка дискового пространства, которые препятствуют установке (слишком большого количества) дополнительных файлов.

Кажется, это работает для некоторых тестовых файлов, которые я установил в соответствии с вашими примерами.Однако использование 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";
    }
}

РЕДАКТИРОВАТЬ: Используйте решение tsee (выше), оно гораздо более продумано.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top