将具有awk的大文件拆分为具有定义数量的多行记录的块

时间:2018-09-14 08:08:00

标签: bash awk

我想将一个大文件(> 15G,几百万条记录)分块为更小的块,并按定义的记录数进行。我正在使用Ubuntu 16.04。

以下是规则:

  1. 对于可移植性问题,我想坚持使用UNIX命令。
  2. 有一个特定的模式定义输入文件中每个记录(“ $$$$”)的结尾。
  3. 应保留此模式以将记录分成大块
  4. 每个块应包含n条记录
  5. 每条记录的行数都可以不同。

我搜索了类似的问题like this one,但找不到确切的答案。

这是输入文件语法的示例。

example.sdf

Item1
  Mrv171c009131823372D          

  2  1  0  0  0  0            999 V2000
   -3.7946    2.9241    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -2.9708    2.9673    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
  1  2  1  0  0  0  0
M  END
> <property_1>
3

$$$$
Element2
  Mrv171c009131823372D          

  2  1  0  0  0  0            999 V2000
   -3.6161    1.7634    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -2.7956    1.8496    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
  1  2  1  0  0  0  0
M  END
> <property_1>
5

$$$$
Something3
  Mrv171c009131823372D          

  2  1  0  0  0  0            999 V2000
   -3.0580    0.5134    0.0000 N   0  0  0  0  0  0  0  0  0  0  0  0
   -3.5772    1.1545    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
  1  2  1  0  0  0  0
M  END
> <property_1>
10

$$$$

n = 2所需的输出:

example.sdf.chunk000001

Item1
  Mrv171c009131823372D          

  2  1  0  0  0  0            999 V2000
   -3.7946    2.9241    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -2.9708    2.9673    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
  1  2  1  0  0  0  0
M  END
> <property_1>
3

$$$$
Element2
  Mrv171c009131823372D          

  2  1  0  0  0  0            999 V2000
   -3.6161    1.7634    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
   -2.7956    1.8496    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
  1  2  1  0  0  0  0
M  END
> <property_1>
5

$$$$

example.sdf.chunk000002

Something3
  Mrv171c009131823372D          

  2  1  0  0  0  0            999 V2000
   -3.0580    0.5134    0.0000 N   0  0  0  0  0  0  0  0  0  0  0  0
   -3.5772    1.1545    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
  1  2  1  0  0  0  0
M  END
> <property_1>
10

$$$$

此刻,我尝试通过split和awk实现此功能(请参见下文),但这看起来很笨拙。我也尝试看过csplit,但是找不到任何在每个块中设置定义数量的记录的选项。

拆分

split命令工作得很好,但是不接受'$$$$'分隔符,因为它是多个字符。我可以通过用单个字符(@)替换此模式来使其工作,但是如果在SDF文件中找到另一个字符,则可能会出错。

# replace the separator with a dummy
sed -e 's/\$\$\$\$/@/g' export.sdf > example.sdf.tmp
# split the file (3 records) into smaller chunks (xaa, xab, ect.) with max 2 records
split -t @ -l 2 example.sdf.tmp
# replace the dummy with the proper separator
for f in xa*; do tail -n +2 $f |sed 's/@/\$\$\$\$/g' > $f.fixed; done

不幸的是,在编辑输入文件以及随后的每个块时,它看起来并不是很优化,因此我尝试使用awk。

awk

我是awk的新手,但我设法做到了:

awk 'NR%2==1 {x=sprintf(".chunk%06d",++i);} END {printf "%s",$0} {print>FILENAME x}' RS="\\$\\$\\$\\$" ORS="\$\$\$\$" example.sdf

第一个块看上去完全是我要寻找的东西,但是第二个有两个错误:

example.sdf.chunk000002

[ blank line ]     
Something3
  Mrv171c009131823372D          

  2  1  0  0  0  0            999 V2000
   -3.0580    0.5134    0.0000 N   0  0  0  0  0  0  0  0  0  0  0  0
   -3.5772    1.1545    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
  1  2  1  0  0  0  0
M  END
> <property_1>
10

$$$$
$$$$

如您所见,在文件的开头有一个空行(我无法显示,因此我键入了[空白行]),在最后一块的末尾有一个最终的结束模式。我还尝试了一个具有9条记录的文件,在第2-5块的开头得到了空行,在第5块的末尾得到了最后一个额外的'$$$$'。

如何解决此问题,以便获得预期的输出?

任何帮助将不胜感激!

乔斯·曼努埃尔

1 个答案:

答案 0 :(得分:0)

使用GNU awk:

awk -v RS='\\$\\$\\$\\$\n' -v nb=2 -v c=1 '
{
   file=sprintf("%s%s%06d",FILENAME,".chunk",c)
   printf "%s%s",$0,RT > file 
}
NR%nb==0 {c++}
' example.sdk

模式RS的记录分隔符$$$$允许立即获取全部块。

变量nb保存每个文件的块数,c是文件名的计数数。