У меня есть большой файл и список моих конкретных строк. Вывод не должен содержать мои конкретные строки и еще одну после каждой из них. Два последовательных совпадения невозможны из-за структуры файла, который я хочу отфильтровать. Например,
Конкретные строки:
'ggg'
'sss'
Вход:
'ggg'
'123'
'rrr'
'321'
'sss'
'666'
Выход:
'rrr'
'321'
Простой grep -v -A 1
не работает
Нет, это всего лишь пример, строки не заключаются в кавычки. Мне нужны только точные совпадения, поэтому математика «ррр» точна «ррр».
Что, если во втором файле есть две последовательные строки, соответствующие строке первого файла? Вы просто пропускаете их или пропускаете и следующий? Пример: строка 1: 'ggg'
, строка 2: sss
, строка 3: 123
, вы пропускаете 123
или нет?
Две последовательные математические операции невозможны из-за структуры файла, который я хочу отфильтровать. У меня есть строка с идентификатором, затем строка с информацией об этом элементе, затем повтор
@ЖеняГончаров ок, спасибо. Пожалуйста, отредактируйте свой вопрос и добавьте эту важную информацию, чтобы будущие читатели могли понять все, что им нужно.
Этот вопрос был закрыт как дубликат другого вопроса, который сильно отличается (в другом вопросе есть только один файл, мы печатаем строку или нет на основе следующей строки в уникальном файле...) Это может быть дубликат, но не цитируемого вопроса. Я голосую за возобновление работы.
Предположения:
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
, а поведение конечного автомата:
skip == 1
, установите состояние skip == 0
и перейдите к следующей строке, не печатая текущую (это строка, следующая за соответствующей).skip == 1
и перейдите к следующей строке, не печатая текущую (она совпадающая).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'
) необходимо читать из файла, а не жестко запрограммировать в скрипте
Это может сработать для вас (GNU sed):
sed 's#.*#/^&$/ba#' lines | sed -f - -e 'b;:a;N;d' file
Используйте файл строк для создания входного файла регулярных выражений sed для каждой строки входных строк.
Передайте регулярные выражения через канал в вызов sed, который ничего не делает, если регулярные выражения не совпадают. В противном случае добавьте следующую строку и удалите как соответствующую, так и следующую строку.
все строки всегда заключаются в одинарные кавычки? вам нужны точные совпадения или допустимы совпадения подстроки, например, должно ли
'rrr'
совпадать с'rrrrr'
? вам нужны точные совпадения строк или допустимы совпадения подстрок, например, должно ли'rrr'
соответствовать одной строке, которая выглядит как'aaa' 'rrr' 'ggg'
?