Как удалить соответствующую строку, строку выше и одну ниже, используя sed?

У меня есть следующая последовательность, встречающаяся несколько раз в файле:

yyyy
xxxx
zzzz

У меня есть регулярное выражение, которое соответствует xxxx. Всякий раз, когда есть совпадение, я хочу удалить эту строку, линию до (например, yyyy) и строку после нее (например, zzzz). Как я могу использовать sed для этого?

Ответ 1

Трюк состоит в том, чтобы сохранить последнюю строку, видимую в "удержании".

sed -n '
/^xxxx/{n
        n
        x
        d
       }
x
1d
p
${x
  p
 }
' <input file>

Начиная с x - свопинг текущей строки ввода с пространством удержания (x), тогда для первой строки ничего не печатайте (1d), последующие строки печатают строку, только что замененную (p), в последней строке снова поменяйте место удержания и распечатайте то, что было в нем ($x{x p}). Это оставляет то, что нужно делать, когда мы попадаем на целевую линию (начиная с /^xxxx/) - читайте следующие две (n n) и поменять пространство с пространством удержания (x) - это оставляет пространство удержания со следующей строкой, которую мы хотим распечатать, и пространство с рисунком с линией перед матчем, которое мы не хочу, поэтому мы отключаем его (d)

Ответ 2

Вы можете проверить этот документ. Он охватывает использование sed для работы с несколькими строками.

Ответ 3

Вот как я сделал бы это в perl, может быть, он может помочь вам отправить вас на правильный путь... Удачи!

open(INFILE,"<in.txt");
my(@arrayOutBoundData, $skipNextLine)l
for (<INFILE>) {
    if (not $skipNextLine) {
        if (/^xxxx$/) {
            pop(@arrayOutBoundData);
            $skipNextLine = 1;
        } else {
            push(@arrayOutBoundData,$_);
        }
    }
$skipNextLine = 0
}

open(OUTFILE,">out.txt");
for (@arrayOutBoundData) {
    print OUTFILE;
}

(Не проверено без perl на этой системе, пожалуйста, простите за любой сайт.)

Ответ 4

Это может сработать для вас (GNU sed):

echo -e "a\nyyyy\nxxxx\nzzzz\nb" | sed 'N;/^xxxx/M{/^xxxx/d;$!N;d};P;D'
a
b

Это сохраняет окно двух строк в пространстве шаблонов, и если требуемое регулярное выражение найдено в первой или второй строке, читает следующую строку и затем удаляет все три строки. Редкие случаи - если регулярное выражение найдено в первой или последней строках, когда нет линии до/после. В этих случаях можно удалить только две строки.

Кстати, это решение, возможно, выявило возможную ошибку в GNU sed. Флаг M адреса позволяет использовать метасимволы ^ и $ в качестве маркеров нулевой длины в регулярном выражении для начала и конца строки в многострочных строках. Пустой адрес // повторно использует ранее указанный адрес. Должен ли этот адрес быть одним, который включает многострочный флаг? В настоящее время он, как представляется, включает флаг, даже если он не указан i.e.

sed 'N;/^xxxx/M{/^xxxx/d;$!N;d};P;D' file

производит другой (правильный) результат:

sed 'N;/^xxxx/M{//d;$!N;d};P;D' file

если xxxx появляется во второй строке файла.

Ответ 5

Вы можете использовать следующее:

sed -n '/xxxx/{N;s/.*//;x;d;};x;p;${x;p;}'

Это заменит 3 строки одной пустой строкой.

Ответ 6

Вы можете сначала отменить файл, использовать sed, чтобы удалить согласованную строку и следующую строку (или строки, +Nd в sed) и, наконец, отменить результат:

tac old.file | sed -e '/xxxx/,+1d' | tac > new.file

Ответ 7

grep -v -f <(grep -1 "xxxx" file) file