Как удалить все совпадающие строки и одну после каждой из них?

У меня есть большой файл и список моих конкретных строк. Вывод не должен содержать мои конкретные строки и еще одну после каждой из них. Два последовательных совпадения невозможны из-за структуры файла, который я хочу отфильтровать. Например,

Конкретные строки:

'ggg'
'sss'

Вход:

'ggg'
'123'
'rrr'
'321'
'sss'
'666'

Выход:

'rrr'
'321'

Простой grep -v -A 1 не работает

все строки всегда заключаются в одинарные кавычки? вам нужны точные совпадения или допустимы совпадения подстроки, например, должно ли 'rrr' совпадать с 'rrrrr'? вам нужны точные совпадения строк или допустимы совпадения подстрок, например, должно ли 'rrr' соответствовать одной строке, которая выглядит как 'aaa' 'rrr' 'ggg'?

markp-fuso 24.07.2024 16:07

Нет, это всего лишь пример, строки не заключаются в кавычки. Мне нужны только точные совпадения, поэтому математика «ррр» точна «ррр».

Женя Гончаров 24.07.2024 16:15

Что, если во втором файле есть две последовательные строки, соответствующие строке первого файла? Вы просто пропускаете их или пропускаете и следующий? Пример: строка 1: 'ggg', строка 2: sss, строка 3: 123, вы пропускаете 123 или нет?

Renaud Pacalet 24.07.2024 16:45

Две последовательные математические операции невозможны из-за структуры файла, который я хочу отфильтровать. У меня есть строка с идентификатором, затем строка с информацией об этом элементе, затем повтор

Женя Гончаров 24.07.2024 16:51

@ЖеняГончаров ок, спасибо. Пожалуйста, отредактируйте свой вопрос и добавьте эту важную информацию, чтобы будущие читатели могли понять все, что им нужно.

Renaud Pacalet 24.07.2024 16:53

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

Renaud Pacalet 24.07.2024 17:03
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
130
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

Предположения:

  • мы ищем точные совпадения строк, включая пробелы, знаки препинания и кавычки.
  • совпадения могут встречаться в последовательных строках, и в этом случае мы игнорируем все совпадения плюс следующую несовпадающую строку (ПРИМЕЧАНИЕ: OP добавил комментарий, в котором говорится, что совпадения последовательных строк невозможны; см. конец ответа для упрощенного сценария awk)

Общий подход:

  • если мы находим совпадающую строку, мы игнорируем текущую строку и устанавливаем флаг, чтобы игнорировать следующую строку
  • если флаг установлен, мы игнорируем текущую строку и снимаем флаг
  • в противном случае мы печатаем текущую строку

Пример входного файла:

$ cat input
'ggg'                       # match/ignore and 
'123'                       # ignore
'rrr'
'321'
'sss'                       # match/ignore and 
'666'                       # ignore
'aaa' 'ggg' 'xxx'
'12345'
'xxx'                       # match/ignore and
'xxx'                       # match/ignore and
98352                       # ignore
'xyz'
hello world

Пример набора строк для сопоставления (и игнорирования):

$ cat lines
'ggg'              # will not match on the line: 'aaa' 'ggg' 'xxx'
'sss'
rrr                # will not match on 'rrr' because of the missing quotes
'xxx'              # will match on consecutive lines and skip the next non-matching line

ПРИМЕЧАНИЕ. Комментарии в файлах не существуют.

Одна awk идея:

awk '
#### 1st file:

FNR==NR { a[$0];  next }       # save line as index in array a[]

#### 2nd file:

$0 in a { skip=1; next }       # if line is an index in array then set the "skip" flag and ignore this line

skip    { skip=0; next }       # if flag is set then clear flag and ignore this line

1                              # otherwise print current line
' lines input

######
# or as a one-liner

awk 'FNR==NR {a[$0];next} $0 in a {skip=1;next} skip {skip=0;next} 1' lines input

Это генерирует:

'rrr'
'321'
'aaa' 'ggg' 'xxx'
'12345'
'xyz'
hello world

ПРИМЕЧАНИЕ. Если предположения неверны и/или это не работает для реальных файлов OP, тогда нам потребуется обновить вопрос с использованием более репрезентативного набора данных.


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

awk '
FNR==NR { a[$0];   next }       # 1st file: save line as index in array a[]
$0 in a { getline; next }       # 2nd file: if line is an index in array then get next line (and ignore) then skip to next input line otherwise ...
1                               # print current line
' lines input

######
# or as a one-liner

awk 'FNR==NR {a[$0];next} $0 in a {getline;next} 1' lines input

Если мы удалим одну из строк 'xxx' из файла input, это сгенерирует:

'rrr'
'321'
'aaa' 'ggg' 'xxx'
'12345'
'xyz'
hello world

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

В любом POSIX awk вы можете легко реализовать конечный автомат, который сделает это при чтении второго файла:

awk 'FNR == NR {a[$0]; next}
     skip == 1 {skip = 0; next}
     $0 in a   {skip = 1; next}
               {skip = 0; print}' file1 file2

При анализе первого файла (FNR == NR) мы сохраняем строки в ассоциативном массиве a и переходим к следующей строке (next). При анализе второго файла мы находимся либо в состояниях skip == 1, либо skip == 0. Начальное состояние — skip == 0, а поведение конечного автомата:

  1. Если мы находимся в состоянии skip == 1, установите состояние skip == 0 и перейдите к следующей строке, не печатая текущую (это строка, следующая за соответствующей).
  2. В противном случае, если текущая строка соответствует строке из первого файла, установите состояние skip == 1 и перейдите к следующей строке, не печатая текущую (она совпадающая).
  3. В противном случае установите состояние skip == 0 и напечатайте текущую строку (это не совпадающая строка и не строка, следующая за совпадающей).

Используя любой awk:

$ awk 'NR==FNR{lines[$0]; next} $0 in lines{c=2} !(c&&c--)' lines input
'rrr'
'321'

Если вы хотите пропустить 15 строк вместо 2, измените 2 на 15.

См. также Печать с помощью sed или awk строки по соответствующему шаблону, чтобы узнать о похожих идиомах.

Вот awk-скрипт:

function check(line) { return \
line != "'ggg'" && \
line != "'sss'"
}
check($0) && check(prev) {print $0}
{prev = $0}

А также Perl-скрипт:

open(FH, '<', $ARGV[0]) or die;
while(<FH>){
    push @spec, $_;
}
close(FH);
open(FH, '<', $ARGV[1]) or die;
while($line = <FH>){
    if (not (grep /$line/, @spec) and (length $prev <= 0 or not (grep /$prev/, @spec))) {
        print $line;
    }
    $prev = $line;
}
close(FH);

Он принимает 2 аргумента командной строки: файл с определенными строками и входной файл.

строки соответствия ('ggg' и 'sss') необходимо читать из файла, а не жестко запрограммировать в скрипте

markp-fuso 24.07.2024 18:17

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

sed 's#.*#/^&$/ba#' lines | sed -f - -e 'b;:a;N;d' file

Используйте файл строк для создания входного файла регулярных выражений sed для каждой строки входных строк.

Передайте регулярные выражения через канал в вызов sed, который ничего не делает, если регулярные выражения не совпадают. В противном случае добавьте следующую строку и удалите как соответствующую, так и следующую строку.

Другие вопросы по теме