为 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
我正在尝试使用 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