sed:パターンを見つけて同じ行の別のパターンに置き換える

sed:パターンを見つけて同じ行の別のパターンに置き換える

1行にgene_idと遺伝子名を含むファイルがあります。次の言葉に変えたいです。遺伝子ID後ろの言葉遺伝子またはそれ以降製品またはそれ以降スプロット(一部欠落している場合があります。)

以下は一行の例です。

chrM    Gnomon  CDS 8345    8513    .   +   1   gene_id "cds-XP_008824843.3"; transcript_id "cds-XP_008824843.3"; Parent "rna-XM_008826621.3"; Dbxref "GeneID:103728653_Genbank:XP_008824843.3"; Name "XP_008824843.3"; end_range "8513,."; gbkey "CDS"; gene "semaphorin-3F"; partial "true"; product "semaphorin-3F"; protein_id "XP_008824843.3"; sprot "sp|Q13275|SEM3F_HUMAN";
chrM    StringTie   exon    2754    3700    .   +   .   gene_id "cds-YP_007626758.1"; transcript_id "cds-YP_007626758.1"; Parent "gene-ND1"; Dbxref "Genbank:YP_007626758.1,Gene "ID:15088436"; Name "YP_007626758.1"; Note "TAAstopcodoniscompletedbytheadditionof3'AresiduestothemRNA"; gbkey "CDS"; gene "ND1"; product "NADHdehydrogenasesubunit1"; protein_id "YP_007626758.1"; transl_except "(pos:3700..3700%2Caa:TERM)"; transl_table "2";

私は以下を達成するためにsedを使用しようとしました。

sed -E 's/[^gene_id] .*?;/[^gene] .*?;|[^sprot] .*?;|[^product] .*?;/g'

しかし、結果は正しくありません。

chrM    Gnomon  CDS 8345    8513    .   +   1   gene_id "cds-XP_008824843.3"[^gene] .*?;|[^sprot] .*?;|[^product] .*?;
chrM     StringTie       exon    2754    3700    .       +       .       gene_id "cds-YP_007626758.1"[^gene] .*?;|[^sprot] .*?;|[^product] .*?;

ところで、行をすべて保存したいのですが、その後ろに単語があります。遺伝子ID、このように:

chrM    Gnomon  CDS 8345    8513    .   +   1   gene_id "semaphorin-3F"; transcript_id "cds-XP_008824843.3"; Parent "rna-XM_008826621.3"; Dbxref "GeneID:103728653_Genbank:XP_008824843.3"; Name "XP_008824843.3"; end_range "8513,."; gbkey "CDS"; gene "semaphorin-3F"; partial "true"; product "semaphorin-3F"; protein_id "XP_008824843.3"; sprot "sp|Q13275|SEM3F_HUMAN";
chrM     StringTie       exon    2754    3700    .       +       .       gene_id "ND1"; transcript_id "cds-YP_007626758.1"; Parent "gene-ND1"; Dbxref "Genbank:YP_007626758.1,Gene "ID:15088436"; Name "YP_007626758.1"; Note "TAAstopcodoniscompletedbytheadditionof3'AresiduestothemRNA"; gbkey "CDS"; gene "ND1"; product "NADHdehydrogenasesubunit1"; protein_id "YP_007626758.1"; transl_except "(pos:3700..3700%2Caa:TERM)"; transl_table "2";

または次のようになります(他のものを見逃した場合)。

chrM    Gnomon  CDS 8345    8513    .   +   1   gene_id "sp|Q13275|SEM3F_HUMAN"; transcript_id "cds-XP_008824843.3"; Parent "rna-XM_008826621.3"; Dbxref "GeneID:103728653_Genbank:XP_008824843.3"; Name "XP_008824843.3"; end_range "8513,."; gbkey "CDS"; gene "semaphorin-3F"; partial "true"; product "semaphorin-3F"; protein_id "XP_008824843.3"; sprot "sp|Q13275|SEM3F_HUMAN";
chrM     StringTie       exon    2754    3700    .       +       .       gene_id "ND1"; transcript_id "cds-YP_007626758.1"; Parent "gene-ND1"; Dbxref "Genbank:YP_007626758.1,Gene "ID:15088436"; Name "YP_007626758.1"; Note "TAAstopcodoniscompletedbytheadditionof3'AresiduestothemRNA"; gbkey "CDS"; gene "ND1"; product "NADHdehydrogenasesubunit1"; protein_id "YP_007626758.1"; transl_except "(pos:3700..3700%2Caa:TERM)"; transl_table "2";

どんな助けでも大変感謝します。

答え1

gene次のperlスクリプトは、各入力ラインで、およびを順番に一致させようとしますproduct(つまり、製品より遺伝子を、sprotsよりも製品を優先します)。sprotそのうちの1つが一致すると、一致する単語が抽出されます。単語が二重引用符で囲まれているとします。

一致するものが見つかったら、gene_id次の単語を抽出された単語に置き換えます。

変更の有無にかかわらず、対応する行が印刷されます。

#!/usr/bin/perl

while (<>) {
  my $word = '';

  if (m/\b(?:gene)\s+("[^"]*")/) {
    $word = $1;
  } elsif (m/\b(?:product)\s+("[^"]*")/) {
    $word = $1;
  } elsif (m/\b(?:sprot)\s+("[^"]*")/) {
    $word = $1;
  };

  if ($word) {
    s/\bgene_id\s+(?:"[^"]*")/gene_id $word/
  };

  print;
} 

あるいは、ループを使用して一致するキーワードを繰り返すように作成することもできます。

#!/usr/bin/perl

while (<>) {
  my $word = '';

  foreach my $match (qw(gene product sprot)) {
    if (m/\b(?:$match)\s+("[^"]*")/) {
      $word = $1;
      last; # first match wins, exit this loop
    }
  };

  if ($word) {
    s/\bgene_id\s+(?:"[^"]*")/gene_id $word/
  };

  print;
}

IMO、このバージョンは読みやすく理解しやすいので、より良いです(特にループはforeach単語リストを繰り返すことを強調します)。さらに、$word = $1ステートメントを繰り返す必要はありません。ステートメントを変更したり追加のコードを追加する必要がある場合は、3回ではなく1回だけ実行すると、間違いの可能性が低くなります。 「繰り返しないでください」は、このような小規模プログラムではそれほど重要ではありませんが、大規模プログラムでは非常に重要です。それにもかかわらず、重複を避けたり最小化したりするのは良いプログラミング習慣です。

一致する順序が重要でない場合(たとえば、どの項目が検索されるかを問わない場合は、その項目のみが検索されます)、スクリプトを簡素化できます。

#!/usr/bin/perl

while (<>) {
  my ($word) = m/\b(?:gene|product|sprot)\s+("[^"]*")/;

  if ($word) {
    s/\bgene_id\s+(?:"[^"]*")/gene_id $word/
  };

  print;
} 

使用するスクリプトのバージョンに関係なく、次のように保存しreplace.plて実行可能にしますchmod +x replace.pl。またはすべて、、、replace1.plreplace2.pl試してみてくださいreplace3.pl。次に、次のように実行します。

$ ./replace.pl input.txt 
chrM    Gnomon  CDS 8345    8513    .   +   1   gene_id "semaphorin-3F"; transcript_id "cds-XP_008824843.3"; Parent "rna-XM_008826621.3"; Dbxref "GeneID:103728653_Genbank:XP_008824843.3"; Name "XP_008824843.3"; end_range "8513,."; gbkey "CDS"; gene "semaphorin-3F"; partial "true"; product "semaphorin-3F"; protein_id "XP_008824843.3"; sprot "sp|Q13275|SEM3F_HUMAN";
chrM    StringTie   exon    2754    3700    .   +   .   gene_id "ND1"; transcript_id "cds-YP_007626758.1"; Parent "gene-ND1"; Dbxref "Genbank:YP_007626758.1,Gene "ID:15088436"; Name "YP_007626758.1"; Note "TAAstopcodoniscompletedbytheadditionof3'AresiduestothemRNA"; gbkey "CDS"; gene "ND1"; product "NADHdehydrogenasesubunit1"; protein_id "YP_007626758.1"; transl_except "(pos:3700..3700%2Caa:TERM)"; transl_table "2";

答え2

与えられたキーに複数の値が適用されると、最後の値が最終値になるハッシュの属性を利用します。

perl -lpe 'my($l,%h)=($_);
  $h{gene_id}=$_ for map {
     $l =~ /\b$_\s+(".*?");/
  } reverse qw(gene product sprot);
  s/\bgene_id\s+\K".*?";/$h{gene_id};/;
' your_file_genes

コマンドはすべて同じで名前のみが変更されるため、フィールド名のみを提供し、forループが残りを処理するワークシート全体を簡単に駆動できます。

for i in gene product sprot;do
  cat - <<\_FMT_ |\
  sed -e "s/%s/$i/"
s/(\<gene_id\s+)"[^"]*"(.*\s%s\s+("[^"]*"))/\1\3\2/;t
_FMT_
done | sed -Ef - your_file_genes

答え3

ソリューションを完了するには、perl以下を使用しますsed。与えられた構文がどのように機能するかはわかりませんが、実際には文字列に一致する正規表現が必要です。

... gene_id "remove me" ... some other stuff gene "replacement" ... more stuff
    =======                                  ====
    gene_id   "[^"]*"        .*              gene    "[^"]*"

gene_idそしてgeneそれはそれ自体で一致します。二重引用符で囲まれた文字列は、[^"]*二重引用符、二重引用符以外の文字()、および他の二重引用符を連結したものです。最後に、あなたの間に何かがあります.*

\(\)これで、交換品にリサイクルする必要がある部品を配置する必要があります。

sed 's/gene_id "[^"]*"\(.* gene \("[^"]*"\)\)/gene_id \2\1/'

外部ペアには、同じままである必要があるすべてが含まれています。これは\1交換用に再利用できます。内部ペアは再利用したい文字列ですgene_id

product今や代替代替として使用したい場合は、拡張正規表現のsprot代替文字列を使用できます。

sed -E 's/gene_id "[^"]*"(.*(gene|product|sprot) ("[^"]*"))/gene_id \3\1/'

ただし、これはoverを好むことはなくgene、存在する最後の項目を好みます。その優先順位を取得するには別のステップが必要で、最後のステップから始めてより良いステップに置き換えることができます。productsprot

sed 's/gene_id "[^"]*"\(.* sprot \("[^"]*"\)\)/gene_id \2\1/
     s/gene_id "[^"]*"\(.* product \("[^"]*"\)\)/gene_id \2\1/
     s/gene_id "[^"]*"\(.* gene \("[^"]*"\)\)/gene_id \2\1/'

geneあるいは、sprot`の順序がproduct固定されていることがわかっている場合は、実際の行を予約済みスペースに駐車しながら、優先IDを最初に抽出できます。

sed -E 'h;s/(sprot|product|gene) ("[^"]*").*/#\2/;s/.*#//;G;s/(.*)\n(.*gene_id )"[^"]*"/\2\1/' 

タグは、#IDの一部として不明な文字列である可能性があります。 GNUの場合はsedこれを使用して\n確認できます。したがって、上記の文字列の最初の行をトークンに置き換え、残りの行を削除してからトークンの前のすべての項目を削除すると、パターンスペースにはIDだけが残ります。その後、G元の行(保持バッファに保持するために使用)が追加され、それに続く内容がhID(改行の前部)に置き換えられます。とにかく説明するよりも書く方が簡単です。"string"gene_id

関連情報