sed: удерживайте шаблон и переставьте линию - PullRequest
4 голосов
/ 07 июня 2011

Я не уверен, смогу ли я сделать это чисто с помощью sed:

Я пытаюсь переставить строки, подобные этой

GF:001,GF:00012,GF:01223<TAB>XXR
GF:001,GF:00012,GF:01223,GF:0666<TAB>XXXR3

GF:001<TAB>XXR
GF:00012<TAB>XXR
GF:01223<TAB>XXR
GF:001<TAB>XXXR3
GF:00012<TAB>XXXR3
GF:01223<TAB>XXXR3
GF:0666<TAB>XXXR3

Anyoneкакие-нибудь намеки?Кардинальность GF: XXXX чередуется, так как длина GF: XXXX равна.

Я застрял с sed -n ' '/\(XX.*\)$/' { s/,/\t\1\n/ }' input, но я не могу сослаться на первоначально сопоставленный шаблон в первую очередь.есть идеи?ура!

Обновление: я думаю, что это невозможно сделать, просто используя sed.Поэтому я использовал Perl для этого:

perl -e 'open(IN, "< file");
while (<IN>) {
    @a = split(/\t/);
    @gos = split(/,/, $a[0]);
    foreach (@gos) {
      print $_."\t".$a[1];
    }
close( IN );' > output

Но если кто-нибудь знает способ решить эту проблему только с помощью sed, пожалуйста, опубликуйте его здесь ...

Ответы [ 5 ]

7 голосов
/ 07 июня 2011

Это может быть сделано в sed, хотя я, вероятно, использовал бы Perl (или Awk или Python) для этого.

Я не претендую на элегантность этого решения, но грубая сила и невежество иногда окупаются. Я создал файл с именем sed.script, который не является оригинальным и содержит:

/\(GF:[0-9]*\),\(.*\)<TAB>\(.*\)/{
:redo
s/\(GF:[0-9]*\),\(.*\)<TAB>\(.*\)/\1<TAB>\3@@@@@\2<TAB>\3/
h
s/@@@@@.*//
p
x
s/.*@@@@@//
t redo
d
}

Я запустил это как:

sed -f sed.script input

где input содержит две строки, показанные в вопросе. Произвел вывод:

GF:001<TAB>XXR
GF:00012<TAB>XXR
GF:01223<TAB>XXR
GF:001<TAB>XXXR3
GF:00012<TAB>XXXR3
GF:01223<TAB>XXXR3
GF:0666<TAB>XXXR3

(Я позволил себе намеренно неверно истолковать <TAB> как последовательность из 5 символов вместо одного символа табуляции; вместо этого можно легко исправить ответ, чтобы обработать фактический символ табуляции.)

Объяснение сценария sed:

  • Поиск строк с более чем одним вхождением GF:nnn, разделенных запятыми (нам не нужно обрабатывать строки, содержащие одно такое вхождение). Делайте остальную часть сценария только на таких строках. Все остальное пропускается (печатается) без изменений.
  • Создайте метку, чтобы мы могли вернуться к ней
  • Разделите линию на 3 запомненных части. Первая часть - это исходная информация GF; вторая часть - любая другая информация GF; третья часть - это поле после <TAB>. Замените это первое поле, <TAB>, третье поле, неправдоподобный шаблон маркера (@@@@@), второе поле, <TAB>, третье поле.
  • Скопируйте измененную строку в область удержания.
  • Удалить маркер до конца.
  • Печать.
  • Поменять пространство удержания на пространство шаблона.
  • Удалите все, вплоть до шаблона маркера.
  • Если мы выполнили какую-либо работу, вернитесь к метке redo.
  • Удалите то, что осталось (оно уже было напечатано).
  • Конец блока скрипта.

Это простой цикл, который уменьшает количество шаблонов на один на каждой итерации.

3 голосов
/ 07 июня 2011

Вы можете сделать это напрямую с помощью awk:

$ awk '{gsub(/,/, "\t" $NF "\n");print}' input 

В этом случае мы просто заменим запятую на вкладку, соединенную с последним полем (NF хранит количество полей записи;$NF получает NF -ое поле) с новой строкой.Затем выведите результат.

Он также может быть решен с помощью sed аналогичным образом, но ИМХО, немного лучше, чем решение Джонатана (которое довольно сложное, я должен заметить).

sed -n '
:BEGIN
 h
 s/,.*<TAB>/<TAB>/
 p
 x
 s/^[^,]*,//
t BEGIN' input

Здесь мы определяем метку в начале скрипта:

:BEGIN

Затем копируем содержимое пространства шаблона в пространство удержания:

h

Теперь мы заменяем все от первой запятой до вкладки только вкладкой:

 s/,.*<TAB>/<TAB>/

Мы печатаем результат ...

p

... иполучить содержимое пространства удержания:

x

Поскольку мы напечатали первую строку - которая содержит первый шаблон GF:XXX, за которым следует последний шаблон XXR - мы удаляем первый шаблон GF:XXX изстрока:

 s/^[^,]*,//

Если выполняется замена, мы переходим к началу сценария:

t BEGIN

И все снова применяется к той же строке, за исключением того, что теперь эта строкабольше не имеет первого GF:XXX шаблона.OTOH, если замена не сделана, то обработка текущей строки завершена, и мы больше не переходим к началу.

2 голосов
/ 09 января 2013

Ну, мне потребовалось 3 часа, чтобы сделать это

sed -re ':a; s/(GF:[0-9]*[^,]*),([^<]*)(<TAB>[A-Z]*)/\1\3\n\2\3/g;ta; ' file.txt

2 голосов
/ 07 июня 2011

Если вам не нужен sed, awk хорош в этом:

awk -F'\t|,' '{ i=1; do { printf("%s\t%s\n",$i,$NF); i++;}  while ( i<NF ); }' inputfile
1 голос
/ 09 января 2013
awk -F'[,\t]' '{for (i=1;i<NF;i++) print $i"\t"$NF}' file

Awk читает по одной строке за раз (по умолчанию) и разбивает строку на поля. Я использую -F, чтобы сказать awk, чтобы разделить строку на поля в каждой запятой или вкладке. NF - количество полей в строке, $ i - содержимое поля с номером i.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...