don может быть лучше в большинстве случаев, но только в том случае, если файл действительно большой, и вы не можете sed
обработать файл сценария такого большого размера (который может иметь место в более чем 5000 строк сценария) , вот это с простым sed
:
sed -ne:t -e"/\n.*$match/D" \
-e'$!N;//D;/'"$match/{" \
-e"s/\n/&/$A;t" \
-e'$q;bt' -e\} \
-e's/\n/&/'"$B;tP" \
-e'$!bt' -e:P -e'P;D'
Это пример того, что называется скользящим окном при вводе. Он работает путем создания упреждающего буфера $B
строк -count, прежде чем пытаться что-либо напечатать.
И на самом деле, вероятно, я должен уточнить мой предыдущий пункт: основной ограничитель производительности как для этого решения, так и для Дон будет напрямую связан с интервалом. Это решение будет замедляться с большими интервальными размерами , в то время как Дон замедлится с большими интервальными частотами . Другими словами, даже если входной файл очень большой, если фактический интервал встречается все еще очень редко, то его решение, вероятно, является путем. Однако, если размер интервала относительно управляем и, вероятно, встречается часто, то это решение, которое вы должны выбрать.
Итак, вот рабочий процесс:
- Если
$match
он найден в пространстве шаблона, которому предшествует \n
ewline, sed
рекурсивно D
удалит каждую \n
ewline, которая ему предшествует.
- Раньше
$match
я полностью очищал пространство шаблонов - но чтобы легко справиться с перекрытием, оставление ориентира, кажется, работает намного лучше.
- Я также попытался
s/.*\n.*\($match\)/\1/
сделать это за один раз и уклониться от цикла, но когда он $A/$B
большой, D
цикл Elete оказывается значительно быстрее.
- Затем мы вытягиваем
N
строку ввода ext, которой предшествует \n
разделитель ewline, и снова D
пытаемся выбрать /\n.*$match/
один раз, ссылаясь на наше последнее использованное регулярное выражение w / //
.
- Если пространство образца совпадает,
$match
то это может быть сделано только в начале строки $match
- все $B
предыдущие строки были очищены.
- Итак, мы начинаем
$A
циклы после.
- Каждый запуск этого цикла мы будем пытаться
s///
ubstitute для &
себя $A
го \n
символа ewline в пространстве картины, и, в случае успеха, t
Эст произрастет нас - и весь наш $A
осле буфер - из сценария полностью запустить скрипт через сверху со следующей строкой ввода, если есть.
- Если
t
est не увенчался успехом, мы b
вернемся к :t
метке op и вернемся к другой строке ввода - возможно, начнем цикл снова, если $match
произойдет при сборе $A
fter.
- Если пройти в
$match
петлю функции, то мы будем стараться p
Ринт в $
последнюю строку , если это, и если !
не пытаться s///
ubstitute для &
себя $B
го \n
ewline характер в пространстве картины.
- Мы тоже
t
это сделаем , и если это будет успешно, мы перейдем к :P
метке rint.
- Если нет, мы вернемся к
:t
op и добавим еще одну строку ввода в буфер.
- Если мы сделаем это для того, чтобы
:P
набрать текст, мы будем P
печатать, а затем D
подняться до первой \n
строки в шаблонном пространстве и перезапустить сценарий сверху с оставшимся.
И вот на этот раз, если бы мы делали A=2 B=2 match=5; seq 5 | sed...
Пространство шаблона для первой итерации в :P
rint будет выглядеть так:
^1\n2\n3$
И вот как sed
собирает свой $B
efore буфер. И поэтому sed
печатает в выходные $B
строки -count за собранным вводом. Это означает, что, учитывая наш предыдущий пример, sed
будет P
выведен 1
вывод, а затем его D
выбор и отправка обратно в верхнюю часть скрипта пространства шаблона, которое выглядит следующим образом:
^2\n3$
... и в верхней части скрипта N
извлекается строка ввода ext, поэтому следующая итерация выглядит следующим образом:
^2\n3\n4$
И поэтому, когда мы находим первое вхождение 5
во входных данных, пространство шаблона на самом деле выглядит так:
^3\n4\n5$
Затем D
включается цикл Elete, и когда он проходит, это выглядит так:
^5$
И когда N
строка ввода ext sed
выдвигается, нажимает EOF и выходит. К тому времени в нем были только когда-либо P
набранные линии 1 и 2.
Вот пример запуска:
A=8 B=7 match='[24689]0'
seq 100 |
sed -ne:t -e"/\n.*$match/D" \
-e'$!N;//D;/'"$match/{" \
-e"s/\n/&/$A;t" \
-e'$q;bt' -e\} \
-e's/\n/&/'"$B;tP" \
-e'$!bt' -e:P -e'P;D'
Это печатает:
1
2
3
4
5
6
7
8
9
10
11
12
29
30
31
32
49
50
51
52
69
70
71
72
99
100
$A
и / или$B
. Чем больше вы сделаете эти цифры, тем медленнее они станут - но вы можете сделать их достаточно большими.Вы можете использовать
gnu grep
с-A
и-B
для печати именно те части файла, которые вы хотите исключить, но добавьте-n
переключатель, чтобы также печатать номера строк, а затем отформатировать вывод и передать его как командный сценарийsed
для удаления этих строк:Это также должно работать с файлами шаблонов, передаваемыми
grep
через,-f
например:Я думаю, что это можно было бы немного оптимизировать, если бы он объединял любые три или более последовательных номеров строк в диапазоны, например,
2,6d
вместо2d;3d;4d;5d;6d
... хотя, если на входе есть только несколько соответствий, делать это не стоит.Другие способы, которые не сохраняют порядок строк и, скорее всего, медленнее:
with
comm
:comm
требует отсортированного ввода, что означает, что порядок строк не будет сохранен в конечном выводе (если ваш файл уже не отсортирован), поэтомуnl
используется для нумерации строк перед сортировкой,comm -13
печатает только строки, уникальные для 2-го ФАЙЛА, а затемcut
удаляет часть, которая была добавленаnl
(то есть первое поле и разделитель:
)с
join
:источник
comm
более быстрым решением, чем оригинальное сsed
иgrep
?Если вы не возражаете против использования
vim
:-Nes
включает несовместимый бесшумный режим ex. Полезно для написания сценариев.+{command}
скажите vim запустить{command}
файл.g/${PAT}/
- на всех совпадающих линиях/fff/
. Это сложно, если шаблон содержит специальные символы регулярного выражения, которые вы не намеревались обрабатывать таким образом..-${B}
- с 1 строки над этой.+${A}
- на 2 строки ниже этой (см.:he cmdline-ranges
для этих двух)d
удалить строки.+w !tee
затем пишет в стандартный вывод.+q!
выходит без сохранения изменений.Вы можете пропустить переменные и использовать шаблон и числа напрямую. Я использовал их только для ясности цели.
источник
Как насчет (используя GNU
grep
иbash
):Здесь мы находим строки, от которых нужно отказаться
grep -B2 -A1 'fff' file.txt
, а затем используем это как входной файл, чтобы найти нужные строки, отбрасывающие их.источник
kos
и (теперь удаленное) решение, как если бы во входном файле были повторяющиеся строки, и некоторые из них выходили за пределы диапазона, а другие находились внутри этого диапазона, и все они будут удалены. Кроме того, при множественном вхождении шаблона , если есть строки, как--
во входном файле (за пределами диапазонов), это приведет к их удалению, потому что--
вgrep
выходных данных появляется разделитель, когда более чем одна строка соответствует шаблону (последняя крайне маловероятна, но стоит упоминание, я думаю).Вы можете получить достаточно хороший результат, используя временные файлы:
Результат достаточно хорош, потому что вы можете потерять некоторые отступы в процессе, но если это файл, не чувствительный к XML или отступам, это не должно быть проблемой. Поскольку этот сценарий использует ram-диск, запись и чтение этих временных файлов выполняется так же быстро, как и работа в памяти.
источник
Также, если вы просто хотите исключить некоторые строки перед указанным маркером, вы можете использовать:
(Гленн Джекман на /programming//a/1492538 )
При помощи некоторых команд вы можете получить поведение до / после:
источник
awk
обратный файл, чтобы обрабатывать следующие строки, когда вы хотите повлиять на строки до и перевернуть результат.Один из способов сделать это, возможно, самый простой - создать переменную и выполнить следующие действия:
Таким образом, у вас все еще есть ваша структура. И вы можете легко увидеть из одного вкладыша, что вы пытаетесь удалить.
источник
Если есть только 1 совпадение:
В противном случае (awk):
источник