我正在尝试编写一个脚本,该脚本包含一个包含文本文件的目录(其中384个),并修改具有特定格式的重复行,以使它们不重复。
特别是,我有一些文件,其中一些行以'@'
字符开头并包含子串0:0
。这些行的子集重复一次或多次。对于那些重复的内容,我想将0:0
替换为i:0
,其中i从1开始并递增。
到目前为止,我已经编写了一个bash脚本,找到以'@'
开头的重复行,将它们写入文件,然后将其读回并在while循环中使用sed
进行搜索,替换要替换的第一个出现的行。如下所示:
#!/bin/bash
fdir=$1"*"
#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个文件,我不知道为什么。
我的问题是来自哪里的错误以及如何更有效地执行此操作?我愿意使用其他编程语言或改变我的方法。
修改
奇怪的是,循环在一个文件上运行良好。基本上我跑了
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.5米的文件需要多个小时一样。我想知道是否有更实用的方法。
关于文件删除和其他错误我不知道发生了什么可能是它在内存冲突时遇到了什么?当它们并行运行时会发生什么?
示例输入:
@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
答案 0 :(得分:1)
这里有一些代码可以从您的示例输入中生成所需的输出。
同样,假设您的输入文件按第一个值排序(直到第一个空格字符)。
time awk '{
#dbg if (dbg) print "#dbg:prev=" prev
if (/^@/ && prev!=$1) {fixNum=0 ;if (dbg) print "prev!=$1=" prev "!=" $1}
if (/^@/ && (prev==$1 || NR==1) ) {
prev=$1
n=split($1,tmpArr,":") ; n++
#dbg if (dbg) print "tmpArr[6]="tmpArr[6] "\tfixNum="fixNum
fixNum++;tmpArr[6]=fixNum;
# magic to rebuild $1 here
for (i=1;i<n;i++) {
tmpFix ? tmpFix=tmpFix":"tmpArr[i]"" : tmpFix=tmpArr[i]
}
$1=tmpFix ; $0=$0
print $0
}
else { tmpFix=""; print $0 }
}' 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,您应该能够将代码块复制/粘贴到终端窗口cmd-line中,并在末尾替换file > fixFile
,并使用您的真实文件名和固定文件的新名称。回想一下,awk 'program' file > file
(实际上,任何...file>file
)将截断现有的file
然后尝试写入,因此您可能会丢失尝试使用相同名称的文件的所有数据。< / p>
可能会有一些语法改进会减少此代码的大小,并且可能会有一两件事可以使代码更快,但这应该会非常快。如果没有,请发布应在运行结束时出现的time
命令的结果,即
real 0m0.18s
user 0m0.03s
sys 0m0.06s
IHTH
答案 1 :(得分:0)
#!/bin/bash
i=4
sort $1 | 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" >> $1"fixed.txt"
else
echo "$in" >> $1"fixed.txt"
fi
let "i+=1"
done < $1