为 bash 中每第 n 次出现的字符串拆分一个 mol2 文本文件并打印到子文件

Splitting a mol2 text file for every nth occurrences of a string in bash and printing to subfiles

我正在尝试使用 bash 分离包含数百万个 mol2 格式分子的文本文件。下面显示了一个代码示例。重复的字符串是 @<TRIPOS>MOLECULE 所以我想用它来每第 n 次出现就写一个子文件。大约每 30,000 次。我试着在下面写这个脚本,但没有写入子文件

@<TRIPOS>MOLECULE
ZINC000169688935
54    57    1     0     0
SMALL
USER_CHARGES
@<TRIPOS>ATOM
1      C1     -1.3065    0.9799    0.3206 C.3      1 ZINC000169688935   -0.1500
2      C2     -1.4308   -0.5375    0.4726 C.3      1 ZINC000169688935   -0.0500
...
@<TRIPOS>MOLECULE
ZINC000022925452
49    49    1     0     0
SMALL
USER_CHARGES
@<TRIPOS>ATOM
1      N1     -3.9528    1.8161   -2.8615 N.am     1 ZINC000022925452   -1.2400
2      S2     -2.7113    2.7835   -3.3766 S.o2     1 ZINC000022925452    2.7000
...
#!/bin/bash

FILENAME="450mw_comp.mol2"
mol_counter="0"
file_counter="0"
new_file="$FILENAME.$file_counter"

cat $FILENAME
IFS=' '
while read -r line;
do
printf "$line" >> $new_file
if "$line" == *"@<TRIPOS>MOLECULE"*
then
     mol_counter=$((mol_counter+1))

if [ $mol_counter -eq 100 ]
then
    file_counter=$((file_counter+1))
    mol_counter=0
fi

done

如注释中所述,不推荐在 bash 循环中解析数百万行,因为它会非常慢。

注意:请参阅答案的第二部分以获取创建样本输入文件的代码

一个awk想法:

fname='450mw_comp.mol2'
n=2                                                  # limit each output file to 2 molecules

awk -v n="${n}" '

/^@<TRIPOS>MOLECULE/ { molcount++                    # pattern match? increment molecule counter and ...
                       if (molcount % n == 1) {      # if counter modulo n == 0 then ...
                          close(outfile)             # close current output file and ...
                          sfx=sprintf("%05d",++c)    # create new suffix (0-padded 5-digit #) and ...
                          outfile=FILENAME "." sfx   # create new output file name
                       }
                     }
                     { print [=10=] > outfile }
' "${fname}"

备注:

  • 删除注释以整理代码
  • 根据需要更改 bash 变量 n(例如,n=30000
  • 根据需要调整输出文件名后缀宽度(%05)

验证:

$ fname='450mw_comp.mol2'

$ ls -l "${fname}"*
-rw-rw----+ 1 username None 2794 Feb 17 09:28 450mw_comp.mol2
-rw-rw----+ 1 username None  508 Feb 17 09:28 450mw_comp.mol2.00001
-rw-rw----+ 1 username None  508 Feb 17 09:28 450mw_comp.mol2.00002
-rw-rw----+ 1 username None  508 Feb 17 09:28 450mw_comp.mol2.00003
-rw-rw----+ 1 username None  508 Feb 17 09:28 450mw_comp.mol2.00004
-rw-rw----+ 1 username None  508 Feb 17 09:28 450mw_comp.mol2.00005
-rw-rw----+ 1 username None  254 Feb 17 09:28 450mw_comp.mol2.00006

$ cat "${fname}".00001
@<TRIPOS>MOLECULE
ZINC000022XXX001
49    49    1     0     0
SMALL
USER_CHARGES
@<TRIPOS>ATOM
1      N1     -3.9528    1.8161   -2.8615 N.am     1 ZINC000022925452   -1.2400
2      S2     -2.7113    2.7835   -3.3766 S.o2     1 ZINC000022925452    2.7000
@<TRIPOS>MOLECULE
ZINC000022XXX002
49    49    1     0     0
SMALL
USER_CHARGES
@<TRIPOS>ATOM
1      N1     -3.9528    1.8161   -2.8615 N.am     1 ZINC000022925452   -1.2400
2      S2     -2.7113    2.7835   -3.3766 S.o2     1 ZINC000022925452    2.7000

$ for f in "${fname}".*
do
    printf "\n###### ${f}\n\n"
    grep '^ZINC' "${f}"
done

###### 450mw_comp.mol2.00001

ZINC000022XXX001
ZINC000022XXX002

###### 450mw_comp.mol2.00002

ZINC000022XXX003
ZINC000022XXX004

###### 450mw_comp.mol2.00003

ZINC000022XXX005
ZINC000022XXX006

###### 450mw_comp.mol2.00004

ZINC000022XXX007
ZINC000022XXX008

###### 450mw_comp.mol2.00005

ZINC000022XXX009
ZINC000022XXX010

###### 450mw_comp.mol2.00006

ZINC000022XXX011

出于演示目的,我们将生成一个包含 11 个样本分子的输入文件;我们将用 XXX 加上一个 0 填充的 3 位计数器来终止每个 ^ZINC... 字符串(我们不用担心用匹配的 ZINC... 字符串更新 ATOM 条目) :

fname='450mw_comp.mol2'

awk '
BEGIN {
for (i=1;i<=11;i++)

printf "@<TRIPOS>MOLECULE\n\
ZINC000022XXX%03d\n\
49    49    1     0     0\n\
SMALL\n\
USER_CHARGES\n\
@<TRIPOS>ATOM\n\
1      N1     -3.9528    1.8161   -2.8615 N.am     1 ZINC000022925452   -1.2400\n\
2      S2     -2.7113    2.7835   -3.3766 S.o2     1 ZINC000022925452    2.7000\n",i
}
' > "${fname}"

验证:

$ head "${fname}"
ZINC000022XXX001
49    49    1     0     0
SMALL
USER_CHARGES
@<TRIPOS>ATOM
1      N1     -3.9528    1.8161   -2.8615 N.am     1 ZINC000022925452   -1.2400
2      S2     -2.7113    2.7835   -3.3766 S.o2     1 ZINC000022925452    2.7000
@<TRIPOS>MOLECULE
ZINC000022XXX002

$ grep '^ZINC' "${fname}"
ZINC000022XXX001
ZINC000022XXX002
ZINC000022XXX003
ZINC000022XXX004
ZINC000022XXX005
ZINC000022XXX006
ZINC000022XXX007
ZINC000022XXX008
ZINC000022XXX009
ZINC000022XXX010
ZINC000022XXX011