Переключение двух строк

Как я могу использовать команды sed H, H, x, g, g и т.д. для замены двух строк?

Например, в файле

START
this is a dog
this is a cat
this is something else
END

Скажем, я хочу поменять "это собака" на "это что-то еще".

Это то, что у меня есть до сих пор:

/this is a dog/{
 h # put to hold space 
}
/this is something else/{
 # now i am stuck on what to do.
}

Ответ 1

Если вы знаете шаблон на каждой из двух строк, которые вы хотите поменять, но не полное содержимое строк, вы можете сделать что-то вроде этого:

sed -n '                     # turn off default printing
    /dog/{                   # if the line matches "dog"
           h                 # put it in hold space
           :a                # label "a" - the top of a loop
           n                 # fetch the next line
           /something/{      # if it matches "something"
                       p     # print it
                       x     # swap hold and pattern space
                       bb    # branch out of the loop to label "b"
           }                 # done with "something"
                             # if we're here, the line doesn't match "something"
           H                 # append pattern space to hold space
           x                 # swap hold and pattern space
           s/\([^\n]*\)\n\([^\n]*\)$/\2\n\1/    # see below
           x                 # swap hold and pattern space
           ba                # branch to the top of the loop to label "a"
    }                        # done with "dog"
    :b                       # label "b" - outside the loop
                             # print lines that don't match and are outside the pair
    p                        # also prints what had been accumulating in hold space
    ' inputfile

Шаблон замещения удерживает "собаку" в конце накопленных строк. Он продолжает заменять последние две строки, которые мы держим в удержании, чтобы "собака" "пузырилась" на дне.

Например, позвольте поставить еще одну строку после строки "cat", чтобы процесс был немного яснее. Мы будем игнорировать строки перед "собакой" и после "что-то". И я буду продолжать ссылаться на строки, используя мои ники

this is a dog
this is a cat
there a bear here, too
this is something else

"Собака" читается, затем выбирается "cat". Некоторые добавления и замены выполняются. Теперь пространство шаблонов выглядит так (\N представляет новую строку, я использую верхний регистр "N", поэтому он выделяется, ^ - это начало пространства шаблонов, а $ - это конец):

^this is a dog\Nthis is a cat$

Команда подстановки ищет любое количество символов, которые не являются символами новой строки (и захватывает их), за которым следует новая строка, за которой следует любое количество символов, которые не являются символами новой строки (и фиксируют их), которые находятся в конце строки ($ ) и заменяет все это двумя захваченными строками в обратном порядке, разделенными новой строкой. Теперь пространство шаблона выглядит следующим образом:

^this is a cat\Nthis is a dog$

Теперь мы обмениваемся и читаем новую строку. Это не "что-то", поэтому мы делаем некоторые добавления и замены, и теперь у нас есть:

^this is a cat\Nthis is a dog\Nthere a bear here, too$

Мы снова делаем замену и получаем:

^this is a cat\Nthere a bear here, too\Nthis is a dog$

Почему мы не получили "медведь/собака/кошка" вместо этого? Поскольку шаблон регулярного выражения, состоящий из двух строк (каждый из которых, как обычно, состоит из строк, не связанных с новой строкой, за которыми следует новая строка), привязывается в конце строки с помощью $, поэтому мы игнорируем все, что приходит перед ним. Обратите внимание, что последняя строка новой строки подразумевается и фактически не существует в шаблоне или пространстве удержания. Вот почему я не показываю его здесь.

Теперь мы читаем "что-то" и печатаем его. Мы поменяемся местами. Привет! там то, что мы "пузырялись". Филиал и печать. Поскольку "собака" находится в нижней части строк (которые были накоплены в удержании), и мы напечатали "что-то" прямо перед этой связкой, эффект заключается в том, что мы поменяли местами две линии.

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

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

Ответ 2

/this is a dog/{
    h # put line in hold space
    s//this is something else/ # replace with 'this is something else'
    b # branch to end of the script
}
/this is something else/x # swap hold space with pattern space

Так как sed - редактор потоков, вы действительно не можете заменить текущую строку содержимым будущих строк. Однако вы можете сделать обратное и заменить будущие строки на строку, которую вы уже видели, и то, что делает мой script.

Возможно, другим способом было бы использовать команду N для добавления каждой строки в пространство шаблонов, а затем выполнить две команды s для замены двух строк литерала.