Supprimer la string d'un champ particulier en utilisant awk / sed

J'ai un file (> 80 000 lignes) qui ressemble à ceci:

chr1 GTF2GFF chromosome 1 249213345 . . . ID=chr1;Name=chr1 chr1 GTF2GFF gene 11874 14408 . + . ID=DDX11L1;Note=unknown;Name=DDX11L1 chr1 GTF2GFF exon 11874 12227 . + . Parent=NR_046018_1 chr1 GTF2GFF exon 12613 12721 . + . Parent=NR_046018_1 chr1 GTF2GFF exon 13221 14408 . + . Parent=NR_046018_1 chr1 GTF2GFF gene 14362 29370 . - . ID=WASH7P;Note=unknown;Name=WASH7P chr1 GTF2GFF exon 14362 14829 . - . Parent=NR_024540 chr1 GTF2GFF exon 14970 15038 . - . Parent=NR_024540 chr1 GTF2GFF exon 15796 15947 . - . Parent=NR_024540 chr1 GTF2GFF exon 16607 16765 . - . Parent=NR_024540 chr1 GTF2GFF exon 16858 17055 . - . Parent=NR_024540 chr1 GTF2GFF exon 17233 17368 . - . Parent=NR_024540 chr1 GTF2GFF exon 17606 17742 . - . Parent=NR_024540 chr1 GTF2GFF exon 17915 18061 . - . Parent=NR_024540 chr1 GTF2GFF exon 18268 18366 . - . Parent=NR_024540 chr1 GTF2GFF exon 24738 24891 . - . Parent=NR_024540 chr1 GTF2GFF exon 29321 29370 . - . Parent=NR_024540 chr1 GTF2GFF gene 34611 36081 . - . ID=FAM138A;Note=unknown;Name=FAM138A chr1 GTF2GFF exon 34611 35174 . - . Parent=NR_026818 chr1 GTF2GFF exon 35277 35481 . - . Parent=NR_026818 

et je veux extraire seulement les lignes qui contiennent «gène» dans le troisième champ et réorganiser le 9e champ pour contenir seulement la valeur d'ID (par exemple, DDX11L1). C'est le résultat souhaité:

 chr1 11874 14408 DDX11L1 . + chr1 14362 29370 WASH7P . - chr1 34611 36081 FAM138A . - 

En utilisant awk j'ai obtenu les champs désirés facilement:

 head -20 genes.gff3 | awk '$3=="gene" {print $1 "\t" $4 "\t" $5 "\t" $9"\t" $6 "\t" $7}' chr1 11874 14408 ID=DDX11L1;Note=unknown;Name=DDX11L1 . + chr1 14362 29370 ID=WASH7P;Note=unknown;Name=WASH7P . - chr1 34611 36081 ID=FAM138A;Note=unknown;Name=FAM138A . - 

Mais j'ai du mal à get la valeur de l'ID. J'ai essayé de le canaliser vers sed:

 head -20 genes.gff3 | awk '$3=="gene" {print $1 "\t" $4 "\t" $5 "\t" $9"\t" $6 "\t" $7}' | sed 's/\(^.+\t\)ID=\(\w+\).+\(\t.+$\)/\1\2\3/g' 

et aussi gsub

 head -20 genes.gff3 | awk '$3=="gene" {gsub(/\(^.+\t\)ID=\(\w+\).+\(\t.+$\)/, "\1\2\3", $9); print $1 "\t" $4 "\t" $5 "\t" $9"\t" $6 "\t" $7}' 

Mais le résultat est le même que l'utilisation de awk seul. Comment puis-je extraire la valeur de l'ID? Je sens que je suis vraiment proche d'une solution ici.

À votre santé.

Vous pouvez split le champ et utiliser substr en:

 split($9, a, ";") print substr(a[1], 4) 

Les index Awk commencent à 1 .

Une autre option pourrait être de modifier le séparateur de champs d'input ( FS ). FS est l'espace, "", par défaut – qui a aussi l'effet spécial d' ignorer les espaces de début et de fin .

En outre, au lieu d'utiliser print $1, \t, ... ou la variante printf on pourrait placer OFS sur tab.


Exemples:

Modification de FS:

 awk -F" +|;|=" ' $3 == "gene" { printf("%s\t%s\t%s\t%s\t%s\t%s\t\n", $1, $4, $5, $10, $6, $7); } ' data.file 

Utilisation de la division:

 awk ' $3 == "gene" { split($9, a, ";") printf("%s\t%s\t%s\t%s\t%s\t%s\t\n", $1, $4, $5, substr(a[1], 3), $6, $7); } ' data.file 

OFS et FS:

Le séparateur de champ de sortie ( OFS ) comme onglet, et le FS alternatif à l'intérieur de awk. Également mis à jour FS à inclure onglet:

 awk ' BEGIN { FS="[ \t]+|;|=" OFS="\t" } $3 == "gene" { print $1, $4, $5, $10, $6, $7 } ' data.file 

Voir aussi les variables Open Group Variables et Special , Exemples .

Manuel de Gawk – il est généralement noté quand les choses sont une extension gawk à awk.

Le séparateur de champ de la fonction split est une expression régulière, vous pouvez donc split = OR ; . Si vous savez que $9 commence par "ID =", alors

 awk -v OFS='\t' ' $3 == "gene" { split($9, id, /[=;]/) print $1, $4, $5, id[2], $6, $7 } ' genes.gff3 

Si "ID =" n'est pas nécessairement au début du champ, il y a encore un peu plus de travail à faire:

 awk -v OFS='\t' ' $3 == "gene" { id = "" len = split($9, f, /[=;]/) for (i=1; i<len; i++) { if (f[i] == "ID") { id = f[i+1] break } } print $1, $4, $5, id, $6, $7 } ' genes.gff3 

C'est une solution Bash, comme l'a permis de publier, malgré la request explicite demandant d'utiliser awk et sed :

 show_genes() { local filename="$1" while read -ra larr; do if [[ ${larr[2]} = gene ]]; then larr[8]="${larr[8]%%;*}" larr[8]="${larr[8]#ID=}" printf '%s\n' "${larr[*]}" fi done < "$filename" } 

Utilisation: show_genes /path/to/some/file.txt

Exemple de sortie:

 [rany$] cat data.txt romosome 1 249213345 . . . ID=chr1;Name=chr1 chr1 GTF2GFF gene 11874 14408 . + . ID=DDX11L1;Note=unknown;Name=DDX11L1 chr1 GTF2GFF exon 11874 12227 . + . Parent=NR_046018_1 chr1 GTF2GFF exon 12613 12721 . + . Parent=NR_046018_1 chr1 GTF2GFF exon 13221 14408 . + . Parent=NR_046018_1 chr1 GTF2GFF gene 14362 29370 . - . ID=WASH7P;Note=unknown;Name=WASH7P chr1 GTF2GFF exon 14362 14829 . - . Parent=NR_024540 chr1 GTF2GFF exon 14970 15038 . - . Parent=NR_024540 chr1 GTF2GFF exon 15796 15947 . - . Parent=NR_024540 chr1 GTF2GFF exon 16607 16765 . - . Parent=NR_024540 chr1 GTF2GFF exon 16858 17055 . - . Parent=NR_024540 chr1 GTF2GFF exon 17233 17368 . - . Parent=NR_024540 chr1 GTF2GFF exon 17606 17742 . - . Parent=NR_024540 chr1 GTF2GFF exon 17915 18061 . - . Parent=NR_024540 chr1 GTF2GFF exon 18268 18366 . - . Parent=NR_024540 chr1 GTF2GFF exon 24738 24891 . - . Parent=NR_024540 chr1 GTF2GFF exon 29321 29370 . - . Parent=NR_024540 chr1 GTF2GFF gene 34611 36081 . - . ID=FAM138A;Note=unknown;Name=FAM138A chr1 GTF2GFF exon 34611 35174 . - . Parent=NR_026818 chr1 GTF2GFF exon 35277 35481 . - . Parent=NR_026818 [rany$] show_genes data.txt chr1 GTF2GFF gene 11874 14408 . + . DDX11L1 chr1 GTF2GFF gene 14362 29370 . - . WASH7P chr1 GTF2GFF gene 34611 36081 . - . FAM138A [rany$] 

Juste une petite pause café

 perl -ne 's/\t.*?\tgene// #remove \t F2 \t gene and s/\S*\tID=(.*?);.*/$1/ #remove \t Fn \t ID=.... keeping the id and print' file