如何获取 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 指出的那样:找到导致问题的线路可能会很有趣。问题行的编号应该是以下命令打印(或写入文件)的编号之一:

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 调用和第二个 awk 调用合并为一个 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 来执行前两个步骤。这是否更快cut … | grep …取决于您的数据和您的实施。在 sed 调用中,\t如果您的实现无法理解,请替换为文本制表符\t;如果您的实现无法理解\n替换s,请用反斜杠换行符替换它。

sed -n -e 's/[ \t].*//' \
    -e 's!/ns/!\n&!' -e 'b' \
    -e 's/^.*\n//p'

/ns/如果第一个字段中始终出现一次,您可以将其简化为以下内容,它与最后一次出现的 匹配/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

相关内容