У меня есть большой файл и список моих конкретных строк. Вывод не должен содержать мои конкретные строки и еще одну после каждой из них. Два последовательных совпадения невозможны из-за структуры файла, который я хочу отфильтровать. Например,
Конкретные строки:
'ggg'
'sss'
Вход:
'ggg'
'123'
'rrr'
'321'
'sss'
'666'
Выход:
'rrr'
'321'
Простой grep -v -A 1
не работает
🤔 А знаете ли вы, что...
С Bash можно управлять процессами, запущенными в фоновом режиме, с помощью управляющих сигналов.
Предположения:
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 аргумента командной строки: файл с определенными строками и входной файл.
Это может сработать для вас (GNU sed):
sed 's#.*#/^&$/ba#' lines | sed -f - -e 'b;:a;N;d' file
Используйте файл строк для создания входного файла регулярных выражений sed для каждой строки входных строк.
Передайте регулярные выражения через канал в вызов sed, который ничего не делает, если регулярные выражения не совпадают. В противном случае добавьте следующую строку и удалите как соответствующую, так и следующую строку.