grep для строк не после шаблона

Aug 17 2020

Я пытаюсь найти все строки файла, не соответствующие определенному шаблону.

Некоторое время у меня была проблема с 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, ...)?

Ответы

ilkkachu Aug 17 2020 at 03:48

Насколько я понимаю, 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следует печатать только одну из каждого набора последовательных одинаковых строк.

jubilatious1 Aug 17 2020 at 18:40

используя 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", которые появятся в вашем выводе, но это не ясно мне, если ваш входной файл когда-либо будет содержать такие последовательные строки].

https://raku.org/