Я обрабатываю огромный файл с помощью (GNU) awk (другие доступные инструменты: инструменты оболочки Linux, некоторые старые (> 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?





Кажется, это работает для некоторых тестовых файлов, которые я настроил в соответствии с вашими примерами. Однако включение 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 (см. Выше), оно гораздо более продуманное.
Использование Perl совершенно не вредит производительности! Вызов команд оболочки с использованием обратных кавычек из perl (как и вы) снижает производительность. Если вы воспользуетесь типичной для оболочки идиомой, состоящей в том, чтобы пропустить вещи через множество программ или вызвать множество дополнительных процессов, вы снизите производительность.
Вы совершенно правы, це. Я имел в виду, что использование Perl именно таким образом ухудшит производительность. Ваш правильно написанный сценарий намного лучше.
Извините за такую резкость, я неправильно понял второе предложение вашего сообщения. Ваше здоровье,
Позвольте мне начать с того, что ваше описание проблемы на самом деле не так уж и полезно. В следующий раз, пожалуйста, будьте более конкретными: вы можете упустить гораздо лучшие решения.
Итак, из вашего описания я понимаю, что у вас есть два файла, которые содержат данные, разделенные пробелами. В первом файле вы хотите сопоставить первые три столбца с некоторым шаблоном поиска. Если он найден, вы хотите найти все строки в другом файле, которые содержат четвертый и пятый столбцы соответствующей строки в первом файле. Из этих строк вам нужно извлечь второй и третий столбцы, а затем распечатать первый столбец первого файла, а второй и третий - из второго файла. Хорошо, вот и:
#!/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
В отличие от решения другого постера, которое содержит множество системных вызовов, оно вообще не возвращается к оболочке и, следовательно, должно быть достаточно быстрым.
Извините за неправильное указание. Я попробую ваше решение на работе. Один вопрос: как решить, что имя другого файла (t.txt в вашем ответе) неизвестно: поэтому мне нужно найти файл, который соответствует моим критериям?
Каковы ваши критерии для имени файла? Что вам следует сделать: используйте File :: Find. Это модуль для рекурсивного просмотра каталогов. Он был в Perl 5.0, так что вы можете спокойно его использовать.
Это намного лучшее решение, чем мой хак, который загружал бы все содержимое обоих greps в память и (вероятно) был бы очень медленным. Было бы неплохо увидеть добавление File :: Find для полного решения.
Это тот тип работы, который в первую очередь заставил меня перейти с awk на Perl. Если вы собираетесь это сделать, вам может быть проще создать сценарий оболочки, который создает сценарий (-ы) awk для запроса и последующего обновления отдельными шагами.
(Я написал такого зверя для чтения / обновления файлов в стиле windows-ini - он уродлив. Хотел бы я использовать perl.)
Я часто вижу ограничение «Я не могу использовать какие-либо модули Perl», а когда это не домашний вопрос, это часто просто из-за недостатка информации. Да, даже вы можете использовать CPAN содержит инструкции о том, как установить модули CPAN локально, не имея привилегий root. Другой вариант - просто взять исходный код модуля CPAN и вставить его в свою программу.
Ничего из этого не помогает, если есть другие неустановленные ограничения, такие как нехватка дискового пространства, которые препятствуют установке (слишком большого количества) дополнительных файлов.
Вы почти правы, за исключением случая - очень строгих системных администраторов - в очень большом банке - в действующей системе - где мне только что позвонили из-за того, что я неправильно регистрирую то, что я сделал с файлом - моим .vimrc - без Интернета. подключение к машине - и я должен попросить админов закачать файлы ...
Не уверен, что могу понять, что вы пытаетесь сделать, на основе вашего примера - не могли бы вы немного прояснить это? По каким критериям вы находите другой файл? Почему вы выводите f2 и f3 из второго файла или это фиксированное требование?