2016-09-05 7 views
2

Хорошо, я нашел похожие ответы на SO, но мой sed/grep/awk fu настолько беден, что я не мог их адаптировать к своей задаче. Что, учитывая этот файл «test.gff»:использовать sed для извлечения двух частей текста сразу из строки

accn|CP014704 RefSeq CDS 403 915 . + 0 ID=AZ909_00020;locus_tag=AZ909_00020;product=transcriptional regulator 
accn|CP014704 RefSeq CDS 928 2334 . + 0 ID=AZ909_00025;locus_tag=AZ909_00025;product=FAD/NAD(P)-binding oxidoreductase 
accn|CP014704 RefSeq CDS 31437 32681 . + 0 ID=AZ909_00145;locus_tag=AZ909_00145;product=gamma-glutamyl-phosphate reductase;gene=proA 
accn|CP014704 RefSeq CDS 2355 2585 . + 0 ID=AZ909_00030;locus_tag=AZ909_00030;product=hypothetical protein 

Я хочу, чтобы извлечь два значения 1) текст справа от «ID =» до запятой и 2) текст справа от «продукта =»до конца строки или точки с запятой (так как вы можете увидеть одну из линий также имеет„ген =“значение

Так что я хочу что-то вроде этого:.

ID product 
AZ909_00020 transcriptional regulator 
AZ909_00025 FAD/NAD(P)-binding oxidoreductase 
AZ909_00145 gamma-glutamyl-phosphate reductase 

Это насколько я получил:

printf "ID\tproduct\n" 

sed -nr 's/^.*ID=(.*);.*product=(.*);/\1\t\2\p/' test.gff 

Спасибо!

+0

'sed' не подходит для таких задач. Используйте 'awk' – sjsam

+0

Данные, которые вы предоставили, не соответствуют шаблону. Например, в 3-х столбцах есть «ген = проА». Будут ли дополнительные факультативные поля? – sjsam

ответ

5

Попробуйте следующее:

sed 's/.*ID=\([^;]*\);.*product=\([^;]*\).*/\1\t\2/' test.gff 

По сравнению с вашей попытки, я изменил способ, которым Вы сопрягать для продукта. Поскольку мы не знаем, заканчивается ли поле ; или EOL, мы просто сопоставляем максимально возможное количество символов не ;. Я также добавил .* в конце, чтобы соответствовать любым возможным оставшимся символам после продукта. Таким образом, когда мы сделаем замену, вся строка будет соответствовать, и мы сможем полностью ее переписать.

Если вы хотите что-то немного более надежными, вот Perl один лайнер:

perl -nle '($id)=/ID=([^;]*)/; ($prod)=/product=([^;]*)/; print "$id\t$prod"' test.gff 

Это извлекает два поля отдельно с использованием регулярных выражений. Он будет работать правильно, даже если поля отображаются в обратном порядке.

+0

perl solution идеально подходит, я думаю. +1 – sjsam

+0

Оба работали красиво! –

1

Если вы GNU-AWK ака gawk в вашем распоряжении, вы можете попробовать что-то, как показано ниже:

С AWK

gawk 'BEGIN{printf "ID\tProduct%s",RS} 
    {printf "%s\t%s%s",gensub(/^.*[[:blank:]]+ID=([^;]*);.*$/,"\\1","1",$0), 
     gensub(/^.*;product=([^;]*)[;]*.*$/,"\\1","1",$0),RS} 
    ' test.gff | expand -t20 

Выход

ID     Product 
AZ909_00020   transcriptional regulator 
AZ909_00025   FAD/NAD(P)-binding oxidoreductase 
AZ909_00145   gamma-glutamyl-phosphate reductase 
AZ909_00030   hypothetical protein 

Как вы заметили, два gensub s выполняют тяжелую атлетику.

  • В gensub(/^.*[[:blank:]]+ID=([^;]*);.*$/,"\\1","1",$0), все, кроме материала, который содержится между ID= и первой запятой, которая следует лишена из записи (см $0). Примечание gensub не изменяет сама запись, а просто возвращает измененную строку, которая печатается.
  • в gensub(/^.*;product=([^;]*)[;]*.*$/,"\\1","1",$0), точно также ничего, кроме материала между product= и первой точкой с запятой (или конца) лишен
  • Наконец мы использовали expand -t, чтобы увеличить ширину вкладки, чтобы получить хорошо отформатированный вывод.
  • Поскольку hardcoding \n - плохая практика, я использовал встроенную переменную разделителя записей RS для печати новой строки после каждой записи.

СЭД решение с использованием аналогичной логики ниже:

Используя патч в

printf "%-20s%s\n" "ID" "Product" 
sed -E "s/^.*[[:blank:]]+ID=([^;]*);.*;product=([^;]*)[;]*.*$/\\1\t\\2/" 39322581 | expand -t20 

Выход

ID     Product 
AZ909_00020   transcriptional regulator 
AZ909_00025   FAD/NAD(P)-binding oxidoreductase 
AZ909_00145   gamma-glutamyl-phosphate reductase 
AZ909_00030   hypothetical protein 

С что вам предоставлено короткое и элегантное решение perl, которое вы, возможно, подумаете над этим, если у вас есть perl в вашем распоряжении.


примечание стороны: Использование \n с Printf делает сценарий менее портативную

0

Еще в AWK. Мы добавляем ";" в список разделителей полей (FS), сдирать строки «ID =» и «продукт =» и печати полей 9 и 10:

$ awk -F'([ \t\n]+|;)' 'BEGIN{print "ID" OFS "Product"}{gsub(/product=|ID=/,""); print $9,$10}' test.gff 
ID Product 
AZ909_00020 locus_tag=AZ909_00020 
AZ909_00025 locus_tag=AZ909_00025 
AZ909_00145 locus_tag=AZ909_00145 
AZ909_00030 locus_tag=AZ909_00030 
1

Основная проблема с регулярным выражением использует .* вместо [^;]* так .* будет соответствовать всем символам, но вы просто хотите совместить не-полуколоны. Попробуйте это:

$ sed -E 's/.*ID=([^;]+).*product=([^;]+).*/\1\t\2/' file 
AZ909_00020  transcriptional regulator 
AZ909_00025  FAD/NAD(P)-binding oxidoreductase 
AZ909_00145  gamma-glutamyl-phosphate reductase 
AZ909_00030  hypothetical protein 

или:

$ awk -F'[=;]' -v OFS='\t' '{print $2, $6}' file 
AZ909_00020  transcriptional regulator 
AZ909_00025  FAD/NAD(P)-binding oxidoreductase 
AZ909_00145  gamma-glutamyl-phosphate reductase 
AZ909_00030  hypothetical protein 

и вы можете извлечь значения заголовков легко с AWK тоже:

$ awk -F'[=;]' -v OFS='\t' 'NR==1{sub(/.* /,"",$1); print $1, $5} {print $2, $6}' file 
ID  product 
AZ909_00020  transcriptional regulator 
AZ909_00025  FAD/NAD(P)-binding oxidoreductase 
AZ909_00145  gamma-glutamyl-phosphate reductase 
AZ909_00030  hypothetical protein 
Смежные вопросы