Ищите строку и печатайте все до и после в пределах диапазона

9

У меня есть этот файл:

sometext1{
string1
}

sometext2{
string2
string3
}

sometext3{
string4
string5
string6
}

Я хочу найти в этом файле определенную строку и распечатать все до этой строки до открытия {и все после этой строки до закрытия }. Я попытался добиться этого с помощью sed, но если я попытаюсь напечатать все в диапазоне, /{/,/string2/например, sed напечатает это:

sometext1{
string1
}

sometext2{
string2
sometext3{
string4
string5
string6
}

Если я ищу строку «string2», мне нужен вывод:

sometext2{
string2
string3
}

Спасибо.

Rodrigo
источник
Что ж, теперь я обнаружил, что мне нужны номера строк выходного файла в исходном файле, чтобы удалить их позже. Я попытался изменить команду, предоставленную @mikeserv, безуспешно, я немного запутался с функцией удержания sed.
Родриго
ну, боже, Родриго, ты никому не говорил об этом, кроме себя. это можно сделать, но лучше всего сделать, как grep -n '' <infile | sed .... Эти sedкоманды будут нужны модифицирование; в частности, биты /адреса, /которые ищут ^якоря верхнего уровня. Таким образом, если вы использовали мой ответ, вероятно , можно сделать следующее : grep -n '' | sed 'H;/{$/h;/^[^:]*:}/x;/{\n.*PATTERN/!d'. Все выходные строки будут иметь префикс с номерами строк исходного файла, за которыми следуют двоеточие, как 1:sometext1{\n2:string1и так далее. sedбудет фильтровать только то, что фильтрует раньше, за исключением того, что каждая строка вывода открывается с номером.
mikeserv

Ответы:

9

Вот две команды. Если вам нужна команда, которая обрезает до последней .*{$строки в последовательности (как это делает @don_crissti ed), вы можете сделать:

sed 'H;/{$/h;/^}/x;/{\n.*PATTERN/!d'

... который работает, добавляя каждую строку к Hстарому \nпробелу после символа ewline, перезаписывая hстарый пробел для каждой совпадающей строки {$, и заменяя hстарый и шаблонный пробел для каждой подходящей строки ^}- и тем самым сбрасывая свой буфер.

Он печатает только строки , которые соответствуют а {затем \newline , а затем PATTERNв какой - то момент - и это только когда - либо произойдет сразу после буфера подкачки.

Это исключает любые строки в серии {$совпадений до последнего в последовательности, но вы можете получить все эти включительно, такие как:

sed '/PATTERN.*\n/p;//g;/{$/,/^}/H;//x;D'

То, что он делает, - это шаблон подстановки и hстарые пробелы для каждой ...{$.*^}.*последовательности, добавляет все строки в последовательности к Hстарому \nпробелу после символа Dewline и заменяет до первого встречающегося \nсимвола ewline в пространстве образца для каждого цикла строки, прежде чем снова начинать с того, что осталось.

Конечно, единственный раз, когда он получает \newline в пространстве шаблонов, это когда строка ввода совпадает ^}- конец вашего диапазона - и поэтому, когда он перезапускает скрипт в любом другом случае, он просто вытягивает следующую строку ввода в обычном режиме.

Однако, когда он PATTERNнаходится в том же пространстве паттерна, что и электронная \nлиния, он печатает партию перед тем, как ^}снова перезаписать ее (чтобы он мог завершить диапазон и очистить буфер) .

Учитывая этот входной файл (спасибо Дон) :

sometext1{
string1
}

sometext2{
PATTERN
string3
}

sometext3{
string4
string5
string6
}

Header{
sometext4{
some string

string unknown

here's PATTERN and PATTERN again
and PATTERN too
another string here
}
}

Первые отпечатки:

sometext2{
PATTERN
string3
}
sometext4{
some string

string unknown

here's PATTERN and PATTERN again
and PATTERN too
another string here
}

... а второй ...

sometext2{
PATTERN
string3
}
Header{
sometext4{
some string

string unknown

here's PATTERN and PATTERN again
and PATTERN too
another string here
}
mikeserv
источник
@don_crissti - я не знаю. Это только разграничивает последовательность для линии начала с }. Это может быть полезно для таких как ... open{\nsub;\n{ command; }\n}; close- но я не уверен, что здесь происходит ...
mikeserv
Привет @mikeserv - у меня есть аналогичный вопрос, который поднимается здесь unix.stackexchange.com/questions/232509/… , ваше решение работает с маленьким файлом, но у меня есть большой файл, и я получаю «Удержание пространства переполнено». сообщение об ошибке. Есть хоть какой-то шанс, знаете ли, как я могу решить это? Большое спасибо
Нарайан Ахадэ
@NarayanAkhade - нет. во всяком случае, не без капитального ремонта. разве ... есть большие пространства ввода, которые не содержатся в {...}блоках? Если это так, и вы используете первое решение, то вы можете сделать это /{$/,/^}/Hв начале, а не просто H. Но если вы также попробовали второе решение и все еще столкнулись с той же ошибкой, это вряд ли поможет, потому что это уже делает это. И не стоит сбрасывать со счетов ed. У Дона очень хороший ответ, и его edможно очень просто использовать для временных файлов буфера , что должно предотвратить переполнение буфера памяти.
mikeserv
6

Вот решение с ed:

ed -s filename <<< $'g/PATTERN/?{?,/}/p\nq\n'

это:

g/PATTERN/     # mark each line matching PATTERN  
?{?,/}/p       # for each marked line, print all lines from the previous { up to the next }  
q              # quit editor

Предполагается, что PATTERNмежду каждой парой есть только одна строка, { }иначе вы получите дублированный вывод для каждой дополнительной строки PATTERNвнутри одного и того же блока.
Он будет работать для нескольких, { }содержащих совпадение одной строки, PATTERNнапример, для тестового файла PATTERNв двух разных разделах:

sometext1{
string1
}

sometext2{
PATTERN
string3
}

sometext3{
string4
string5
string6
}

Header{
sometext4{
some string

string unknown

here's PATTERN again

another string here
}
}

Бег

ed -s sample <<< $'g/PATTERN/?{?,/}/p\nq\n'

выходы:

sometext2{
PATTERN
string3
}
sometext4{
some string

string unknown

here's PATTERN again

another string here
}
don_crissti
источник
Я многое взял от этого, на самом деле! Огромное спасибо!
mikeserv
Я даже не знаю, существует ли эта команда. Спасибо
Родриго
4

С pcregrep:

pcregrep -M '(?s)\{[^}]*PATTERN.*?\}'

Или с GNU grepпри условии, что входные данные не содержат байтов NUL:

grep -Poz '.*(?s)\{[^}]*PATTERN.*?\}'
Стефан Шазелас
источник
0
$ awk 'BEGIN{RS="\n\n"; FS="[{}]"} {if ($2 ~ /string4/) {print $2}}' t1.txt
string4
string5
string6

где:

  • string4 -> Строка для сопоставления
  • t1.txt -> содержит содержимое файла, указанное в запросе
user5337995
источник
-2

sed -n '/ string / p' filename

-n при добавлении в sed подавляет поведение sed по умолчанию, это утверждение может не дать вам именно то, что вы хотите, а просто сместить строку

user2995836
источник