まず、私の問題について詳しく説明します。実はとても簡単です。より正確にするために300GBの巨大な.txtファイルがあり、パターンに一致する最初の列のすべての個別の文字列を別の.txtファイルに入れたいと思います。
awk '{print $1}' file_name | grep -o '/ns/.*' | awk '!seen[$0]++' > test1.txt
これは私が試したものであり、私が見る限りは正常に動作しますが、問題はしばらくすると次のエラーが発生することです。
awk: program limit exceeded: maximum number of fields size=32767
FILENAME="file_name" FNR=117897124 NR=117897124
これほど大きなファイルを解析するための提案はありますか?
awk
が巨大な線にぶつかり、32767以上のフィールドになるように思えます。ただし、awk
では再現できません。
> echo | awk 'BEGIN {for(i=1;i<100000;i++) printf "%d ",i}; { print ""; }' >file
> awk '{ print $50000; }' too_long_line_for_awk.txt
50000
> awk --version
GNU Awk 4.1.0, API: 1.0
長い行に対してより堅牢なツールを使用できます。最初のフィールドの最大長を決定する必要があります。 100と仮定すると、これを試すことができます。
cut -b -100 file | awk ...
さらに(しかしこれはあなたの問題とは無関係です)あなたのawk | grep | awk
構成は意味がありません。それはこのように行われます:
awk '$1 ~ "/ns/" {sub("^.*/ns/","/ns/",$1); if( !seen[$1]++ ) print $1}' \
file_name >test1.txt
デバッグの提案
ラメッシュが指摘したように:問題を引き起こしている線を見つけることは興味深いかもしれません。問題のある行の番号は、次のコマンドによって出力(またはファイルに書き込まれた)番号に続く番号のいずれかである必要があります。
awk '{ print NR;}' | tail -n 1 >crashline.txt
awk
が「クラッシュ」する前にバッファを空にした場合、それは次の数値(+1)である必要があります。
一般的に言って、ツールが専門的であればあるほど、非常に大きなファイルにうまく対処できます。そのファイルをawkで処理できます。組み込みのフィールド処理を使用する代わりに、最初のフィールドを手動で抽出する必要があります。 grep呼び出しと2番目のawk呼び出しを1つのawk呼び出しに結合することもできます。
awk -F '\n' '
{ sub(/[\t ].*/,"");
if (match($0, "/ns/")) $0 = substr($0,RSTART); else next; }
!seen[$0]++
'
ただし、専用ツールを介したパイプラインの方が高速になる可能性があります。フィールドで常にタブを区切り文字として使用する場合は、cut
を使用して最初のフィールドを分離できます。区切り文字がスペースの場合は、cut -d ' '
にします。
cut -f 1 | grep … | …
または、最初の2つの手順でsedを使用することもできます。これがcut … | grep …
よりも速いかどうかは、データと実装によって異なります。 sed呼び出しで、実装が\t
を理解できない場合は、\t
をリテラルのタブ文字に置き換えます。実装がs
置換の\n
を理解しない場合は、バックスラッシュ-改行に置き換えてください。
sed -n -e 's/[ \t].*//' \
-e 's!/ns/!\n&!' -e 'b' \
-e 's/^.*\n//p'
最初のフィールドに/ns/
が常に1回出現する場合は、これを次のように簡略化できます。これは、/ns
の最後の出現と一致します。
sed -n -e 's/[ \t].*//' -e 's!.*/ns/!/ns/!p'
最後のステップに目を向けると、一致するものが多い場合、awkコマンドは大量のメモリを使用します。出力の行の順序を変更してもよい場合は、代わりにsort -u
を使用できます。
cut -f 1 | grep -o '/ns/.*' | sort -u
sed -n -e 's/[ \t].*//' -e 's!.*/ns/!/ns/!p' | sort -u
awk
実装にはフィールド数が制限されているようです。
mawk
の例:
field.c
:
/*------- more than 1 fbank needed ------------*/
/*
compute the address of a field with index
> MAX_SPLIT
*/
CELL *
slow_field_ptr(int i)
{
....
if (i > MAX_FIELD)
rt_overflow("maximum number of fields", MAX_FIELD);
....
}
rt_overflow
(error.c
で定義)は、実行時にエラーメッセージを生成する関数です。
/* run time */
void
rt_overflow(const char *s, unsigned size)
{
errmsg(0, "program limit exceeded: %s size=%u", s, size);
rt_where();
mawk_exit(2);
}
そしてファイルsize.h
:
#define FBANK_SZ 256
#define FB_SHIFT 8 /* lg(FBANK_SZ) */
#else
#define FBANK_SZ 1024
#define FB_SHIFT 10 /* lg(FBANK_SZ) */
#endif
#define NUM_FBANK 128 /* see MAX_FIELD below */
#define MAX_SPLIT (FBANK_SZ-1) /* needs to be divisble by 3 */
#define MAX_FIELD (NUM_FBANK*FBANK_SZ - 1)
ご覧のとおり、MAX_FIELD
のデフォルトは256*128 - 1 = 32767
です。
gawk
を使用すると、この問題を解決できます。