grep для строк не после шаблона
Я пытаюсь найти все строки файла, не соответствующие определенному шаблону.
Некоторое время у меня была проблема с history
использованием GNU bash
(версии 4 и 5), где команды появлялись в дубликатах. Я предположил, что это связано с тем, что в моем у .bashrc
меня была следующая строка:
PROMPT_COMMAND="history -a; history -n; $PROMPT_COMMAND"
и поскольку я использую терминальные мультиплексоры ( screen
и / или tmux
), вышеупомянутая команда выполняется несколько раз (поэтому echo $PROMPT_COMMAND
приводит кhistory -a; history -n; history -a; history -n;
В некоторых ситуациях (особенно при одновременной работе с разными панелями / окнами / фреймами / буферами) последняя введенная мной команда сохранялась дважды или даже чаще в моем ~/.bash_history
. Это привело к появлению таких записей:
#1596110297
yadm list -a | xargs -t ls -l
yadm list -a | xargs -t ls -l
Излишне говорить, что это довольно неприятно. Я просто (надеюсь) нашел исправление для исправление: это НЕ решило проблему с повторяющимися записями в history
-issue (изменив команду на PROMPT_COMMAND="history -a; history -n
), но файле .history
Теперь я хочу избавиться от повторяющихся записей.
Поэтому в настоящее время я пытаюсь найти регулярное выражение для обозначения всего, кроме строк, начинающихся с #
и одной строки после этого. Моя первая идея заключалась в том, чтобы объединить grep -v
(чтобы инвертировать выделение) и grep -A 1
(получить дополнительно одну строку после совпадающего шаблона). Но
grep -v "^#" -A 1 ~/.bash_history
не дало результата, на который я надеялся.
Поэтому мой вопрос: есть ли у кого-нибудь хорошее представление о том, как это сделать grep
? Если нет , как я мог сделать это с другими инструментами ( sed
, awk
, ...)?
Ответы
Насколько я понимаю, grep -v "^#" -A 1
означает печатать строки, которые не начинаются со знака решетки, и по одной строке после каждой. Но вы не хотите , наоборот, напечатать строки , которые действительно начинаются с хэш - знак, и одну строку после?
Учитывая тестовый файл:
#123
echo this
echo this
#456
echo that
echo that
echo that
#789
echo third
grep -A1 ^# history.txt |grep -vxFe --
печатает:
#123
echo this
#456
echo that
#789
echo third
Второй grep
- избавиться от разделителей групп grep -A
отпечатков.
В качестве альтернативы uniq history.txt
следует печатать только одну из каждого набора последовательных одинаковых строк.
используя Raku (урожденная Perl6)
Это похоже на работу для оператора «триггера», доступного для ряда языков сценариев. Ниже приведен ответ с использованием языка программирования Raku (ранее известного как Perl6). Сначала начните с создания более обширного тестового файла:
$ cat repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
B_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
D_yadm list -a | xargs -t ls -l
E_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
G_yadm list -a | xargs -t ls -l
H_yadm list -a | xargs -t ls -l
I_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5
Теперь перейдем к однострочному fff
оператору Raku flip-flop, который реализует поведение, подобное sed. Захват включается для строк, в которых первое регулярное выражение видит (в начале строки ^^
) буквальный символ "#". После включения захват игнорирует первое регулярное выражение и сравнивает его со вторым регулярным выражением, выключаясь, когда он находит совпадение со строками, в которых отсутствует (в начале строки ^^
) символ "#". «Отрицательное» регулярное выражение реализовано в приведенном ниже коде с использованием <-[#]>
отрицательного «Перечислимого класса символов» и реальной особенности языка Raku:
$ raku -ne '.put if /^^ "#" / fff /^^ <-[#]> /;' repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5
Фактически, первое регулярное выражение (слева от fff
инфиксного оператора) может быть записано с <+[#]>
использованием положительного «класса перечислимых символов» для более параллельной конструкции:
$ raku -ne '.put if /^^ <+[#]> / fff /^^ <-[#]> /;' repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5
Кроме того, мне кажется, вы можете улучшить свое регулярное выражение, потребовав сопоставления «#» в начале строки, за которым следует одна или несколько цифр, т. Е. <digit>+
См. Ниже:
$ raku -ne '.put if /^^ <+[#]> <digit>+ / fff /^^ <-[#]> <-digit>+ /;' repeated_log.txt
#1596110297_1
A_yadm list -a | xargs -t ls -l
#1596110297_2
C_yadm list -a | xargs -t ls -l
#1596110297_3
F_yadm list -a | xargs -t ls -l
#1596110297_4
#1596110297_5
[Весь приведенный выше код удаляет повторяющиеся строки, начинающиеся с B, D, E, G, H и I. Единственная особенность, которую я заметил, - это две последовательные целевые строки, такие как "# 1596110297", которые появятся в вашем выводе, но это не ясно мне, если ваш входной файл когда-либо будет содержать такие последовательные строки].