2016-07-14 157 views
3

我已经写在bash一个小程序,转换多个(标签)线,具有六列,成一个单一的线,具有如下面12列:为什么我的程序需要这么长时间才能运行?

$1   $2  $3   $4   $5 $6  $7  $8  $9 $10  $11  $12 
input 
Scaffold952 345718 345781 aug3.g8795.t1 . - 
Scaffold952 346504 346534 aug3.g8795.t1 . - 
Scaffold952 346721 346733 aug3.g8795.t1 . - 
Scaffold952 348148 348241 aug3.g8795.t1 . - 
output 
Scaffold952 345718 345781 aug3.g8795.t1 . - 345718 345718 0 4 63,30,12,93 0,786,1003,2430 

要完成此:

  1. 需要六个输入,根据名称排序(第4栏),然后由起始(COL2)
  2. 生成从输入名称的列表和删除重复在步骤2中 产生
  3. 的foreach线210条
    • 寻找对应于所述输入线
    • 使用列中的两个和三个构建列11和12,将值插入阵列
    • 结合列和阵列,以形成最终的,12列输出
  4. 插入输入到输出文件

第11列由$ 3 - $ 2组成;列12由$ 2减去第一个$ 2值(345718)组成,每列为6行,并打印为csv。

我的代码:

#!/bin/bash 

input=$1 
output=$2 
> $output 
# functions 
function joinArray { local IFS="$1"; shift; echo "$*"; } 
# sort input 
sort -k4,4 -k2,2 -o $input < $input 

    awk '{ print $4 }' $input | uniq | while read -r line; do 
     dup="$(grep -c $line $input)" 
     start="$(grep $line $input | awk 'NR==1 { print $2 }')" 
     records="$(grep $line $input | awk 'NR==1 { print $0 }')" 

     grep $line $input | { 
     while read -r record; do 
      blocksize+=($(awk '{ print $3 - $2 }' <<< "$record")) 
      blockstart+=($(awk -v var="$start" '{ print $2 - var }' <<< "$record")) 
     done 
#  combine input with arrays to form 12 col output 
     bed12[0]+=$(awk '{ print $1 }' <<< "$records") 
     bed12[1]+=$(awk '{ print $2 }' <<< "$records") 
     bed12[2]+=$(awk '{ print $3 }' <<< "$records") 
     bed12[3]+=$(awk '{ print $4 }' <<< "$records") 
     bed12[4]+=$(awk '{ print $5 }' <<< "$records") 
     bed12[5]+=$(awk '{ print $6 }' <<< "$records") 
     bed12[6]+=$(awk '{ print $2 }' <<< "$records") 
     bed12[7]+=$(awk '{ print $2 }' <<< "$records") 
     bed12[8]+='0' 
     bed12[9]+=$dup 
     bed12[10]+=$(joinArray $',' "${blocksize[@]}") 
     bed12[11]+=$(joinArray $',' "${blockstart[@]}") 

     joinArray $'\t' "${bed12[@]}" >> $output 
     } 
    done 

到目前为止,我已经无法有效地使此代码运行,我希望提高它作为一个标准大小的文件(〜30000行),花了三个小时完成。我不确定是什么原因造成了每次写入记录时打开/关闭输出文件的问题;嵌套while循环;数组?这是一个糟糕的程序,语言不是一个合适的选择,或者这是一个大文件(1.7 MB)的预期?

+4

叶gawds。你运行*如何处理每一行的许多外部程序?!你会如何期待**不会变慢?! –

+3

简短形式:可以在bash中编写sorta-kinda体面的软件(使用ksh93更容易),但是您首先遇到了很大的障碍,需要知道自己在做什么。选择不同的语言要容易得多。 –

+0

对于每个脚手架来说,糟糕的'$ input'文件至少读取五次... – Kusalananda

回答

3

有些事情要记住:你把你的while read循环内

  • 一切为了处理每一行运行一次。
  • 运行$(...)执行fork()调用,创建一个全新的进程树条目,在新进程中运行所附的代码,读取其stdout并等待进程完成。这是一个批次的开销。
  • 每次运行awk - 虽然它是一种速度非常快的语言解释程序 - 或grep,但实际上您正在启动一个新程序:将其关闭,动态加载其库依赖项,连接stdin和stdout ...这也是很大的开销。
  • 每次执行重定向时,例如>>foo,即,打开输出文件,其中包含重定向的命令,然后在单个文件完成后再次关闭它。 (这特别适用于外壳语言; >的一些awk实现缓存和重用文件描述符,特别是包括GNU之一)。
  • 不幸的是,在撰写本文时,<<<的bash实现涉及在磁盘上创建一个临时文件。如果采用这种结构的平台,/dev/fd是支持的接口上紧内循环,从而< <(printf '%s' "$foo")可能比<<<"$foo"快 - 虽然我不会真的建议这样做,在实践中,除非你需要它,作为庆典的未来版本有望将解决这种行为,并用易于阅读的语法来做正确的事情。

那么,你能做些什么?

  • 对于工具如awk,使用它们时,才可以有在输入的多行重复使用一个单一的,长时间运行的实例。

    坦率地说,这是这里最重要的建议。将所有工作从bash移动到一个单独的awk调用中,您就完成了。

  • 对于解析输入流分成多个领域,使用bash的自己的内建的:

    read -r first_field second_field third_field ... 
    
  • 在一个封闭的外部范围做你的重定向,要么exec >foo重定向标准输出为整个程序(或整个子shell,如果在这种情况下执行的话),或者在done之后关闭循环以在该循环的持续时间重定向stdout之后放置>foo

+2

不要对'outfile'执行严格循环,而应在'done'后面加入'> outfile'。 – Kusalananda

+0

当然,谢谢。修改。 –

相关问题