現在のファイルの内容に基づいて awk から別のファイルを検索/読み取りすることは可能ですか?
-
02-07-2019 - |
質問
(GNU) で巨大なファイルを処理しています awk
, (その他の利用可能なツールは次のとおりです。Linux シェル ツール、Perl の一部の古い (>5.0) バージョンですが、モジュールをインストールできません)。
私の問題:field1、field2、field3 に X、Y、Z が含まれている場合、field4 と field5 を 1 行に含むファイルを別のディレクトリで検索し、見つかったファイルから現在の出力にデータを挿入する必要があります。
例えば。:
実際のファイル行:
f1 f2 f3 f4 f5
X Y Z A B
次に、(別のディレクトリ内で)別のファイルを検索する必要があります。
f1 f2 f3 f4
A U B W
そしてSTDOUTに書き込みます $0
元のファイルから、そして f2
そして f3
見つかったファイルから抽出し、元のファイルの次の行を処理します。
でできるでしょうか? awk
?
解決
最初に言っておきますが、あなたの問題の説明はあまり役に立ちません。次回は、もっと具体的に教えてください。はるかに優れたソリューションを見逃している可能性があります。
したがって、あなたの説明から、空白で区切られたデータを含む2つのファイルがあることがわかります。最初のファイルでは、最初の 3 つの列を何らかの検索パターンと照合したいとします。見つかった場合は、最初のファイルの一致する行の 4 番目と 5 番目の列を含む、別のファイル内のすべての行を検索します。これらの行から 2 列目と 3 列目を抽出し、最初のファイルの 1 列目を出力し、2 番目のファイルの 2 列目と 3 列目を出力する必要があります。さて、次のようになります。
#!/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 も使用できます には、root 権限を持たずに CPAN モジュールをローカルにインストールする方法が記載されています。もう 1 つの方法は、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 のソリューション (上記) を使用すると、よりよく考えられています。