sed: 패턴을 찾아 같은 줄의 다른 패턴으로 교체

sed: 패턴을 찾아 같은 줄의 다른 패턴으로 교체

한 줄에 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

다음 perl 스크립트는 각 입력 행에서 gene, product및 를 sprot해당 순서대로 일치시키려고 시도합니다(즉, 제품보다 유전자를 우선순위로 두고 sprot보다 제품을 우선순위로 둡니다). 그 중 하나가 일치하면 일치 후의 단어를 추출합니다. 단어는 큰따옴표로 묶인 것으로 간주됩니다.

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명령문 반복을 방지한다는 것입니다. 명령문을 변경하거나 추가 코드를 추가해야 하는 경우, 세 번이 아닌 한 번만 수행하면 되는 경우 오류가 발생할 가능성이 줄어듭니다. "반복하지 마세요"는 이와 같은 사소한 작은 프로그램에서는 그다지 중요하지 않지만 대규모 프로그램에서는 매우 중요할 수 있습니다. 어쨌든 반복을 피하거나 최소화하는 것은 좋은 프로그래밍 습관입니다.

일치 순서가 중요하지 않은 경우(예: 하나만 발견되었더라도 상관하지 않는 경우) 스크립트를 단순화할 수 있습니다.

#!/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.pl, replace2.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/'

그러나 이는 geneover product를 선호하지 sprot않고 존재하는 마지막 항목을 선호합니다. 해당 우선 순위를 원할 경우 별도의 단계가 필요하며 마지막 단계부터 시작하여 더 나은 단계로 대체할 수 있습니다.

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원래 줄( 를 사용하여 보류 버퍼에 보존한 h)이 추가되고 ID(개행 앞 부분)가 "string"이후를 대체합니다 gene_id. 설명하는 것보다 쓰는 것이 더 쉽습니다.

관련 정보