一致する項目に基づいて、異なる文字列を別のファイルの文字列のリストに置き換えます。

一致する項目に基づいて、異なる文字列を別のファイルの文字列のリストに置き換えます。

次のタブ区切り文字を含むファイルがあります。

Chr1    mak   gene    120221  120946  .       +       .       ID=spa-h0003.02;Name=spa-h0003.02
Chr1    mak   mRNA    120221  120946  .       +       .       ID=spa-cap_Chr1_00M;Parent=spa-h0003.02;Name=spa-cap_Chr1_00M
Chr1    mak   exon    120221  120946  .       +       .       Parent=spa-cap_Chr1_00M
Chr1    mak   gene    18546165        18546939        .       +       .       ID=spa-h0004.02;Name=spa-h0004.02
Chr1    mak   mRNA    18546165        18546939        .       +       .       ID=spa-cap_Chr1_18;Parent=spa-h0004.02;Name=spa-cap_Chr1_18
Chr1    mak   exon    18546165        18546504        .       +       .       Parent=spa-cap_Chr1_18
Chr1    mak   exon    18546791        18546939        .       +       .       Parent=spa-cap_Chr1_18

3番目の列に「gene」がある場合にのみ別の文字列を置き換えたいと思います。ただし、9番目の列の文字列は、2番目のファイルの情報に基づいて次のように置き換える必要があります(タブを含む)。

spa-h0003.02  spa-cap_Chr1_00M
spa-h0004.02  spa-cap_Chr1_18

私は何をすべきかわかりません。私は次のように考えています。 (XXは2番目のファイルの情報であるべきですか?)

cat file | awk '$3 == "gene" && $9 == "spa-" {$9 = "XX"} {print}' 

しかし、2番目のファイルの情報をどのように使用しますか?おそらく:

while read n k; do sed -i 's/$n/$k/g' file1; done < fileA

答え1

file1置換したいテキストがあり、file2代替テキストがあり、それらID=の間で検索を実行できると仮定すると、次のawkスクリプトを使用できます(より人気があるようです)。

awk -F'\t' '
  NR==FNR{
    a[$1]=$2                                   # fills the array a with the replacement text
    next
  }
  $3=="gene"{                                  # check only lines with 'gene'
    id=gensub("ID=([^;]*);.*","\\1",1,$9);     # extract the id string
    if(id in a)                                # if the id is part of the array a
       gsub(id,a[id])                          # replace it
  }
  1                                            # print the line
' file2 file1

答え2

人気のない選択:Tcl。 Tclにはstring mapこれを行う素晴らしいコマンドがあります。正確にこれ。残念ながら、Tclは実際にはPerlスタイルのシングルライナー用に作られていません。

echo '
    # read the mapping file into a list
    set fh [open "mapping" r]
    set content [read $fh]
    close $fh
    set mapping [regexp -all -inline {\S+} $content]

    # read the contents of the data file
    # and apply mapping to field 9 when field 3 is "gene"
    set fh [open "file" r]
    while {[gets $fh line] != -1} {
        set fields [split $line \t]
        if {[lindex $fields 2] eq "gene"} {
            lset fields 8 [string map $mapping [lindex $fields 8]]
        }
        puts [join $fields \t]
    }
    close $fh
' | tclsh

awkを使って次のように書く。

awk -F'\t' -v OFS='\t' '
    NR == FNR {repl[$1]= $2; next}
    $3 == "gene" {
        for (seek in repl) 
            while ((idx = index($9, seek)) > 0) 
                $9 = substr($9, 1, idx-1) repl[seek] substr($9, idx + length(seek))
    }
    {print}
' mapping file

関連情報