为大型文本文件操作(重复替换行的迭代替换)编写脚本,奇怪的错误和非常慢。
Writing a script for large text file manipulation (iterative substitution of duplicated lines), weird bugs and very slow.
我正在尝试编写一个脚本,该脚本采用包含文本文件(其中 384 个)的目录并修改具有特定格式的重复行以使它们不重复。
特别是,我有一些文件,其中某些行以 '@'
字符开头并包含子字符串 0:0
。这些行的子集被复制了一次或多次。对于那些重复的,我想用 i:0
替换 0:0
,其中我从 1 开始并递增。
到目前为止,我已经编写了一个 bash 脚本来查找以 '@'
开头的重复行,将它们写入文件,然后读回它们并在一段时间内使用 sed
循环搜索并替换第一次出现的要替换的行。就是下面:
#!/bin/bash
fdir="*"
#for each fastq file
for f in $fdir
do
(
#find duplicated read names and write to file $f.txt
sort $f | uniq -d | grep ^@ > "$f".txt
#loop over each duplicated readname
while read in; do
rname=$in
i=1
#while this readname still exists in the file increment and replace
while grep -q "$rname" $f; do
replace=${rname/0:0/$i:0}
sed -i.bu "0,/$rname/s/$rname/$replace/" "$f"
let "i+=1"
done
done < "$f".txt
rm "$f".txt
rm "$f".bu
done
echo "done" >> progress.txt
)&
background=( $(jobs -p) )
if (( ${#background[@]} ==40)); then
wait -n
fi
done
它的问题是速度慢得不切实际。我 运行 它在一台 48 核计算机上运行了 3 天多,它几乎没有通过 30 个文件。它还似乎删除了大约 10 个文件,我不确定为什么。
我的问题是错误来自哪里,我怎样才能更有效地做到这一点?我愿意使用其他编程语言或改变我的方法。
编辑
St运行gely 循环在一个文件上工作正常。基本上我运行
sort $f | uniq -d | grep ^@ > "$f".txt
while read in; do
rname=$in
i=1
while grep -q "$rname" $f; do
replace=${rname/0:0/$i:0}
sed -i.bu "0,/$rname/s/$rname/$replace/" "$f"
let "i+=1"
done
done < "$f".txt
为了让您了解文件的外观,下面是其中一个文件的几行。问题是,即使它适用于一个文件,它也很慢。好像一个7.5M的文件要几个小时。我想知道是否有更实用的方法。
关于文件删除和其他错误,我不知道发生了什么 可能是 运行 内存冲突或者当它们 运行 并行时发生了什么?
示例输入:
@D00269:138:HJG2TADXX:2:1101:0:0 1:N:0:CCTAGAAT+ATTCCTCT
GATAAGGACGGCTGGTCCCTGTGGTACTCAGAGTATCGCTTCCCTGAAGA
+
CCCFFFFFHHFHHIIJJJJIIIJJIJIJIJJIIBFHIHIIJJJJJJIJIG
@D00269:138:HJG2TADXX:2:1101:0:0 1:N:0:CCTAGAAT+ATTCCTCT
CAAGTCGAACGGTAACAGGAAGAAGCTTGCTTCTTTGCTGACGAGTGGCG
示例输出:
@D00269:138:HJG2TADXX:2:1101:1:0 1:N:0:CCTAGAAT+ATTCCTCT
GATAAGGACGGCTGGTCCCTGTGGTACTCAGAGTATCGCTTCCCTGAAGA
+
CCCFFFFFHHFHHIIJJJJIIIJJIJIJIJJIIBFHIHIIJJJJJJIJIG
@D00269:138:HJG2TADXX:2:1101:2:0 1:N:0:CCTAGAAT+ATTCCTCT
CAAGTCGAACGGTAACAGGAAGAAGCTTGCTTCTTTGCTGACGAGTGGCG
这里有一些代码可以根据您的示例输入生成所需的输出。
同样,假定您的输入文件按第一个值排序(直到第一个 space 字符)。
time awk '{
#dbg if (dbg) print "#dbg:prev=" prev
if (/^@/ && prev!=) {fixNum=0 ;if (dbg) print "prev!==" prev "!=" }
if (/^@/ && (prev== || NR==1) ) {
prev=
n=split(,tmpArr,":") ; n++
#dbg if (dbg) print "tmpArr[6]="tmpArr[6] "\tfixNum="fixNum
fixNum++;tmpArr[6]=fixNum;
# magic to rebuild here
for (i=1;i<n;i++) {
tmpFix ? tmpFix=tmpFix":"tmpArr[i]"" : tmpFix=tmpArr[i]
}
=tmpFix ; [=10=]=[=10=]
print [=10=]
}
else { tmpFix=""; print [=10=] }
}' file > fixedFile
输出
@D00269:138:HJG2TADXX:2:1101:1:0 1:N:0:CCTAGAAT+ATTCCTCT
GATAAGGACGGCTGGTCCCTGTGGTACTCAGAGTATCGCTTCCCTGAAGA
+
CCCFFFFFHHFHHIIJJJJIIIJJIJIJIJJIIBFHIHIIJJJJJJIJIG
@D00269:138:HJG2TADXX:2:1101:2:0 1:N:0:CCTAGAAT+ATTCCTCT
CAAGTCGAACGGTAACAGGAAGAAGCTTGCTTCTTTGCTGACGAGTGGCG
我保留了一些 #dbg:...
语句(但它们现在已被注释掉)以展示您如何 运行 您提供的一小组数据,以及观察变量值的变化。
假设是非 csh,你应该能够 copy/paste 代码块进入终端 window cmd-line 并用你的真实文件名替换末尾的 file > fixFile
以及固定文件的新名称。回想一下 awk 'program' file > file
(实际上,任何 ...file>file
)将 t运行 分类现有的 file
然后尝试写入,因此您可能会丢失文件的所有数据试图使用相同的名称。
可能有一些语法改进会减少此代码的大小,并且可能有 1 或 2 件事可以使代码更快,但这应该 运行 非常快。如果不是,请 post time
命令的结果应该出现在 运行 的末尾,即
real 0m0.18s
user 0m0.03s
sys 0m0.06s
IHTH
#!/bin/bash
i=4
sort | uniq -d | grep ^@ > dups.txt
while read in; do
if [ $((i%4))=0 ] && grep -q "$in" dups.txt; then
x="$in"
x=${x/"0:0 "/$i":0 "}
echo "$x" >> "fixed.txt"
else
echo "$in" >> "fixed.txt"
fi
let "i+=1"
done <
我正在尝试编写一个脚本,该脚本采用包含文本文件(其中 384 个)的目录并修改具有特定格式的重复行以使它们不重复。
特别是,我有一些文件,其中某些行以 '@'
字符开头并包含子字符串 0:0
。这些行的子集被复制了一次或多次。对于那些重复的,我想用 i:0
替换 0:0
,其中我从 1 开始并递增。
到目前为止,我已经编写了一个 bash 脚本来查找以 '@'
开头的重复行,将它们写入文件,然后读回它们并在一段时间内使用 sed
循环搜索并替换第一次出现的要替换的行。就是下面:
#!/bin/bash
fdir="*"
#for each fastq file
for f in $fdir
do
(
#find duplicated read names and write to file $f.txt
sort $f | uniq -d | grep ^@ > "$f".txt
#loop over each duplicated readname
while read in; do
rname=$in
i=1
#while this readname still exists in the file increment and replace
while grep -q "$rname" $f; do
replace=${rname/0:0/$i:0}
sed -i.bu "0,/$rname/s/$rname/$replace/" "$f"
let "i+=1"
done
done < "$f".txt
rm "$f".txt
rm "$f".bu
done
echo "done" >> progress.txt
)&
background=( $(jobs -p) )
if (( ${#background[@]} ==40)); then
wait -n
fi
done
它的问题是速度慢得不切实际。我 运行 它在一台 48 核计算机上运行了 3 天多,它几乎没有通过 30 个文件。它还似乎删除了大约 10 个文件,我不确定为什么。
我的问题是错误来自哪里,我怎样才能更有效地做到这一点?我愿意使用其他编程语言或改变我的方法。
编辑
St运行gely 循环在一个文件上工作正常。基本上我运行
sort $f | uniq -d | grep ^@ > "$f".txt
while read in; do
rname=$in
i=1
while grep -q "$rname" $f; do
replace=${rname/0:0/$i:0}
sed -i.bu "0,/$rname/s/$rname/$replace/" "$f"
let "i+=1"
done
done < "$f".txt
为了让您了解文件的外观,下面是其中一个文件的几行。问题是,即使它适用于一个文件,它也很慢。好像一个7.5M的文件要几个小时。我想知道是否有更实用的方法。
关于文件删除和其他错误,我不知道发生了什么 可能是 运行 内存冲突或者当它们 运行 并行时发生了什么?
示例输入:
@D00269:138:HJG2TADXX:2:1101:0:0 1:N:0:CCTAGAAT+ATTCCTCT
GATAAGGACGGCTGGTCCCTGTGGTACTCAGAGTATCGCTTCCCTGAAGA
+
CCCFFFFFHHFHHIIJJJJIIIJJIJIJIJJIIBFHIHIIJJJJJJIJIG
@D00269:138:HJG2TADXX:2:1101:0:0 1:N:0:CCTAGAAT+ATTCCTCT
CAAGTCGAACGGTAACAGGAAGAAGCTTGCTTCTTTGCTGACGAGTGGCG
示例输出:
@D00269:138:HJG2TADXX:2:1101:1:0 1:N:0:CCTAGAAT+ATTCCTCT
GATAAGGACGGCTGGTCCCTGTGGTACTCAGAGTATCGCTTCCCTGAAGA
+
CCCFFFFFHHFHHIIJJJJIIIJJIJIJIJJIIBFHIHIIJJJJJJIJIG
@D00269:138:HJG2TADXX:2:1101:2:0 1:N:0:CCTAGAAT+ATTCCTCT
CAAGTCGAACGGTAACAGGAAGAAGCTTGCTTCTTTGCTGACGAGTGGCG
这里有一些代码可以根据您的示例输入生成所需的输出。
同样,假定您的输入文件按第一个值排序(直到第一个 space 字符)。
time awk '{
#dbg if (dbg) print "#dbg:prev=" prev
if (/^@/ && prev!=) {fixNum=0 ;if (dbg) print "prev!==" prev "!=" }
if (/^@/ && (prev== || NR==1) ) {
prev=
n=split(,tmpArr,":") ; n++
#dbg if (dbg) print "tmpArr[6]="tmpArr[6] "\tfixNum="fixNum
fixNum++;tmpArr[6]=fixNum;
# magic to rebuild here
for (i=1;i<n;i++) {
tmpFix ? tmpFix=tmpFix":"tmpArr[i]"" : tmpFix=tmpArr[i]
}
=tmpFix ; [=10=]=[=10=]
print [=10=]
}
else { tmpFix=""; print [=10=] }
}' file > fixedFile
输出
@D00269:138:HJG2TADXX:2:1101:1:0 1:N:0:CCTAGAAT+ATTCCTCT
GATAAGGACGGCTGGTCCCTGTGGTACTCAGAGTATCGCTTCCCTGAAGA
+
CCCFFFFFHHFHHIIJJJJIIIJJIJIJIJJIIBFHIHIIJJJJJJIJIG
@D00269:138:HJG2TADXX:2:1101:2:0 1:N:0:CCTAGAAT+ATTCCTCT
CAAGTCGAACGGTAACAGGAAGAAGCTTGCTTCTTTGCTGACGAGTGGCG
我保留了一些 #dbg:...
语句(但它们现在已被注释掉)以展示您如何 运行 您提供的一小组数据,以及观察变量值的变化。
假设是非 csh,你应该能够 copy/paste 代码块进入终端 window cmd-line 并用你的真实文件名替换末尾的 file > fixFile
以及固定文件的新名称。回想一下 awk 'program' file > file
(实际上,任何 ...file>file
)将 t运行 分类现有的 file
然后尝试写入,因此您可能会丢失文件的所有数据试图使用相同的名称。
可能有一些语法改进会减少此代码的大小,并且可能有 1 或 2 件事可以使代码更快,但这应该 运行 非常快。如果不是,请 post time
命令的结果应该出现在 运行 的末尾,即
real 0m0.18s
user 0m0.03s
sys 0m0.06s
IHTH
#!/bin/bash
i=4
sort | uniq -d | grep ^@ > dups.txt
while read in; do
if [ $((i%4))=0 ] && grep -q "$in" dups.txt; then
x="$in"
x=${x/"0:0 "/$i":0 "}
echo "$x" >> "fixed.txt"
else
echo "$in" >> "fixed.txt"
fi
let "i+=1"
done <