2016-07-14 155 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

當然,謝謝。修改。 –

相關問題