300GB txtファイルの最初の列を取得する方法は?

300GB txtファイルの最初の列を取得する方法は?

まず、私の問題を詳しく説明します。実は本当に簡単です。私は巨大な.txtファイル(正確には300GB)を持っており、私のパターンに一致する最初の列のすべての異なる文字列を別の.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

これらの大容量ファイルを解析するための提案はありますか?

答え1

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

デバッグ提案

Rameshが指摘したように、問題を引き起こす線を見つけるのは興味深いかもしれません。問題行の番号は、次のコマンドで印刷された(またはファイルに書き込まれた)番号の1つでなければなりません。

awk '{ print NR;}' | tail -n 1 >crashline.txt

「衝突」の前にバッファを空にした場合は、awk次の数字(+1)でなければなりません。

答え2

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この問題を解決できます。

答え3

一般的に、ツールが特殊化されるほど、非常に大きなファイルをより適切に処理できます。このファイルは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 … | …

または、sedを使用して最初の2つの手順を実行できます。これがより速いかどうかは、cut … | grep …データと実装によって異なります。 sed呼び出しで\t実装が理解できない場合はリテラルタブに、\t実装が理解できない場合はバックスラッシュの改行に\n置き換えます。s

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

関連情報