Text-Processing

將新行轉換為製表符格式的文件

  • March 31, 2022

我有多個文件SRR3384742.Gene.out.tab SRR3384743.Gene.out.tab SRR3384744.Gene.out.tab,按這個順序還有更多。我正在從這些文件中提取第一列和第四列並將其儲存在輸出文件中。我試圖確保當我的腳本讀取一個新文件時,它應該以製表符分隔的方式提取數據,而不是在每個文件的末尾附加數據。

輸入文件:

SRR3384742.Gene.out.tab

N_unmapped      313860  313860  313860
N_multimapping  5786679 5786679 5786679
N_noFeature     286816  31696770        438410
N_ambiguous     1283487 32117   65902
AT1G01010       301     0       301
AT1G01020       623     1       622
AT1G03987       5       5       0
AT1G01030       151     2       149

SRR3384743.Gene.out.tab

N_unmapped      780346  780346  780346
N_multimapping  4621162 4621162 4621162
N_noFeature     182428  28470016        362650
N_ambiguous     1451612 43059   117293
AT1G01010       154     3       151
AT1G01020       685     2       683
AT1G03987       0       0       0
AT1G01030       63      0       63

我得到的輸出:

SRR3384742.Gene.out.tab 
AT1G01010       301
AT1G01020       622
AT1G03987       0
AT1G01030       149
SRR3384743.Gene.out.tab 
AT1G01010       151
AT1G01020       683
AT1G03987       0
AT1G01030       63

所需的輸出:

SRR3384742.Gene.out.tab SRR3384743.Gene.out.tab 
AT1G01010       301     151
AT1G01020       622     683
AT1G03987       0       0
AT1G01030       149     63

我嘗試了以下腳本:

for sample in *Gene.out.tab; do echo -en $sample "\n"; awk 'NR>4 {print $1 "\t" $4}' $sample; awk '{print $0, $sample}' OFS='\t' $sample; done > output

這應該使用 GNU 為您提供註釋中描述的輸出awk

gawk 'FNR==1{names[c++]=FILENAME}
     FNR>4{ lines[$1] = "x"lines[$1] ? lines[$1]"\t"$4 : $4; } 
     END{ 
           for(i=0;i<=c;i++){
               printf "\t%s",names[i]
           } 
           printf "\n"; 
           for(i in lines){ 
               print i,lines[i]
           }
       }' *Gene.out.tab
   SRR3384742.Gene.out.tab SRR3384743.Gene.out.tab
AT1G01010   301 151
AT1G01020   622 683
AT1G01030   149 63
AT1G03987   0   0

而且,為了讓它們在視覺上也很好地對齊,請通過column

$ gawk 'FNR==1{names[c++]=FILENAME}FNR>4{ lines[$1] = "x"lines[$1] ? lines[$1]"\t"$4 : $4; } END{ for(i=0;i<=c;i++){printf "\t%s",names[i];} printf "\n"; for(i in lines){ print i,lines[i]}}' *Gene.out.tab | column -s$'\t' -t
           SRR3384742.Gene.out.tab  SRR3384743.Gene.out.tab
AT1G01010   301                      151
AT1G01020   622                      683
AT1G01030   149                      63
AT1G03987   0                        0

FNR是一個特殊的 awk 變數,它始終保存正在處理的目前文件的行號。FILENAME是一個 GNUawk特殊變數,它保存目前正在處理的文件的名稱。

  • FNR==1{names[c++]=FILENAME}: 如果這是其中一個輸入文件的第一行,則使用該變數c作為names其值為文件名的數組的索引,並將其值遞增 yb 1 ( c++)。處理完所有文件後,files[0]將是第一個文件名,files[1]將是第二個,依此類推。
  • FNR>4{ lines[$1] = "x"lines[$1] ? lines[$1]"\t"$4 : $4; }:這相當於:
if(FNR>4){ 
     if("x"lines[$1]){
      lines[$1]"\t"$4
     else{
         lines[$1] = $4
     }
 }

如果目前輸入文件的行號為 5 或更多,請檢查第一個欄位是否在數組中具有關聯值lines。我們檢查使用"x"lines[$i],因為如果lines[$1]0,那麼測試將是假的,但x0它是真的,所以x可以防止這種情況。因此,如果我們確實有一個值,我們將一個製表符和目前行的第二個欄位附加到它,如果我們沒有一個值,我們將它設置為目前行的第四個欄位。

  • END{ ... }:在處理完所有輸入後執行此操作。
  • for(i=0;i<=c;i++){printf "\t%s",names[i]}; printf "\n"; :列印數組中的每個文件名names,前面有一個製表符。我們希望前導選項卡確保我們在標題行和內容中具有相同數量的欄位。列印文件名後,列印一個換行符。
  • for(i in lines){print i,lines[i]}:對於lines數組的每個索引,列印索引(ID),然後列印在第一步中儲存的相關值。

限制:這需要將所有輸出數據儲存在記憶體中。這在現代機器上真的不應該是一個問題,因為我們只儲存 ID 並且每個文件每個 ID 只儲存一個值,因此它應該能夠在相當不錯的機器上阻塞之前處理大量輸入,但它可能會成為一個大量數據的問題。

引用自:https://unix.stackexchange.com/questions/696418