为大型文本文件操作(重复替换行的迭代替换)编写脚本,奇怪的错误和非常慢。

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 <