web-dev-qa-db-ja.com

300GBのtxtファイルの最初の列を取得するにはどうすればよいですか?

まず、私の問題について詳しく説明します。実はとても簡単です。より正確にするために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

これほど大きなファイルを解析するための提案はありますか?

3
Jovan Andonov

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)である必要があります。

1
Hauke Laging

一般的に言って、ツールが専門的であればあるほど、非常に大きなファイルにうまく対処できます。そのファイルを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_overflowerror.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を使用すると、この問題を解決できます。

0
cuonglm