awkが遅すぎるときにフィールドに基づいて大容量ファイルを分割する最良の方法

awkが遅すぎるときにフィールドに基づいて大容量ファイルを分割する最良の方法

大容量の.gzファイル(500G以上)の処理に問題があります。私の目標は、このファイルの各フィールドを4番目のフィールドに分割することです。私は以前これを行うために素晴らしいawk one-linerを使用しました。

zcat file.txt.gz | awk 'NR>1{print >  $4}'

残念ながら、大容量ファイルの処理に時間がかかりますので、まずサイズ別に分割してから、フィールド別に分割してから各ファイルを連結してみました。以下を使用して分割できます。

i=file.txt.gz
dir=$i
mkdir -p $dir
cd $dir
split -b 200M ../$i $i

for file in `ls *`; do zcat $file | awk 'NR>1{print >  $4}'; done

しかし、4番目のフィールドで正しいファイルをすべてリンクするにはどうすればよいですか?また、本当に良い方法はありませんか?また、「予期しないファイルの終わり」などの内容のgzファイルの分割を使用するとエラーが発生するため、分割も間違っているようですが、提案がある場合は正しい方向に行っているかどうかわかりません。役に立つ。

あなたの助けをいただきありがとうございます!プラ

ベストアンサー1

Satō Katsuraのファイル記述子コメントは、$ 4の他の値が1021以上(通常ユーザーFD制限は1024、stdin / stdout / stderrは-3)以上であると仮定すると正確です。そしてあなたはを使用していますgawk

>ファイルを使用または印刷すると、>>ファイルは明示的になるまで開かれているため、close()スクリプトにFDが蓄積されます。 Gawk v3.0以前から、FD消耗(ulimit -n)は透明に処理されました。開いたファイルのリンクされたリストが検索され、LRU(最も最近使用された)が「一時的に」閉じられます(FDをリリースするためにオペレーティングシステムの観点から閉じます)。gawk内部的には、後で必要に応じて透明に再開できるように追跡されます。-W lint呼び出し時に追加すると(v3.1に基づいて)、これが発生することがわかります。

次のように問題をシミュレートできますbash

printf "%s\n" {0..999}\ 2\ 3\ 0{0..9}{0..9}{0..9} | time gawk -f a.awk

これは$ 4の固有値1,000を含む1,000,000行の出力を生成し、私のラップトップでは約17秒かかります。私の制限は1024 FDです。

 printf "%s\n" {0..499}\ 2\ 3\ {0..1}{0..9}{0..9}{0..9} | time gawk -f a.awk

これはまた、1,000,000行の出力を生成しますが、2,000個の固有値$ 4を含み、約110秒で実行されます(6倍以上の長さ、100万の追加の小さなページエラーを含む)。

$ 4トレースの観点から、上記は出力ファイルがすべての行を変更する「最も悲観的な」入力です(そして、必要な出力ファイルが毎回(再度)開かれることを保証します)。

この問題を解決するのに役立つ 2 つの方法があります。ファイル名の使用の混乱を減らしたり(たとえば、$ 4で事前に並べ替えたり)、GNUを使用して入力をチャンクすることですsplit

事前ソート:

printf "%s\n" {0..499}\ 2\ 3\ {0..1}{0..9}{0..9}{0..9} | 
  sort -k 4 | time gawk -f a.awk

(フィールド番号sortに合わせてオプションを調整する必要があるかもしれません)awk

約4.0秒でファイル処理が最小化されるため、最初のケースよりはるかに高速です。 (大容量ファイルを並べ替えると、$TMPDIRまたはディスクの一時ファイルを使用できます/tmp。)

そしてsplit

printf "%s\n" {0..499}\ 2\ 3\ {0..1}{0..9}{0..9}{0..9} | 
  time split -l 1000 --filter "gawk -f a.awk"

これには約38秒かかります。したがって、1000のプロセスを開始するためのオーバーヘッドがgawk非効率的な内部FD処理よりも少ないと結論付けることができます。この場合>>代わりに使用する必要があります>awkスクリプトでは、そうでなければ、各新しいプロセスは古い出力を破壊します。 (これを呼び出すためにコードの目的を変更すると、同じ警告が適用されますclose()。)

もちろん、次の2つの方法を組み合わせることもできます。

printf "%s\n" {0..499}\ 2\ 3\ {0..1}{0..9}{0..9}{0..9} | 
  time split -l 50000 --filter "sort -k 4 | gawk -f a.awk"

sort私の場合、これには約4秒かかりました。チャンク(50000)を調整すると、プロセス/ファイル処理のオーバーヘッドとディスク使用量の要件をトレードオフできます。 YMMV。

出力ファイルの数を事前に知っていて、あまりにも大きくない場合は、ルートを使用して増やすことができます(たとえば、ulimit -n 8192自分自身su)。あるいは、一般的に制限を調整することもできます。すべてのプロセスで開かれたファイルの制限を増やす方法は?。制限は、オペレーティングシステムとその構成(運が悪い場合はlibcも可能)によって決まります。

おすすめ記事