Если вы знаете шаблон на каждой из двух строк, которые вы хотите поменять, но не полное содержимое строк, вы можете сделать что-то вроде этого:
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's a bear here, too
this is something else
"Собака" читается, затем "кошка" выбирается. Некоторое добавление и замена сделаны. Теперь пространство шаблонов выглядит следующим образом (\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's a bear here, too$
Мы делаем замену снова и получаем:
^this is a cat\Nthere's a bear here, too\Nthis is a dog$
Почему вместо этого мы не получили "медведь / собаку / кошку"? Поскольку шаблон регулярного выражения, состоящий из двух строк (каждая из которых, как обычно, состоит из не- новых строк, за которыми следует новая строка), закрепляется в конце строки с помощью $
, поэтому мы игнорируем все, что предшествует этому. Обратите внимание, что последний символ новой строки подразумевается и фактически не существует в шаблоне или в области хранения. Вот почему я не показываю это здесь.
Теперь мы читаем «что-то» и печатаем это. Мы меняемся. Привет! есть то, что мы "пузырились". Отделение и печать. Так как «собака» находится в нижней части строк (которые были накоплены в удерживающем пространстве), и мы напечатали «что-то» прямо перед этой связкой, в результате мы поменяли местами две строки.
Этот скрипт будет работать независимо от того, сколько строк появилось до, между или после двух строк, которые нужно поменять местами. Фактически, если имеется несколько пар совпадающих линий, члены каждой пары будут поменяться местами по всему файлу.
Как видите, я набираю только одно слово в интересующей строке, но подойдет любое подходящее регулярное выражение.