Я хочу проверить, есть ли в файле журнала экземпляр, в котором две или более последовательных строк содержат один и тот же текст, используя bash. Текст будет уточняться. Временная метка и любой другой текст после третьего поля не должны учитываться при сравнении.
то есть grep... "ошибка" /tmp/file.txt
этот файл будет соответствовать:
2020-01-01 05:05 text1
2020-01-01 05:07 error
2020-01-01 05:15 error
2020-01-01 05:25 error
2020-01-01 05:45 text2
это не будет
2020-01-01 05:05 text1
2020-01-01 05:15 error
2020-01-01 05:25 text2
2020-01-01 05:45 error
2020-01-01 05:05 text3
Есть идеи по использованию grep, sed или awk? В идеале я хотел бы иметь выходное значение 0 для совпадения и 1 для не совпадения.
я не знаком с авк
«Я не знаком с awk» — тогда зачем помечать вопрос awk
?
@Садовник. да, метка времени и любой другой текст игнорируются. Я забыл упомянуть, что соответствующий текст должен быть указан как аргумент.
То есть вам просто нужен ответ да/нет? Да, где-то в файле, неважно где, данный текст встречается на последовательных строках?
@TedLyngmo, глядя на некоторые примеры awk, я подозреваю, что это можно сделать с помощью awk. ты не думаешь?
@МаркРид. Да, но также было бы здорово иметь команду, показывающую совпадающие строки.
@ansanes Я добавил: «Отметка времени и любой другой текст после третьего поля следует игнорировать при сравнении». к вопросу. Вот как я интерпретировал ваш комментарий выше. Это было правильно?
@TedLyngmo Да.
@ansanes Хорошо, тогда я не понимаю, как работает принятый ответ, поскольку он включает текст после третьего поля в сравнении.
@TedLyngmo Не совсем понимаю, что вы имеете в виду. Я принял ответ, потому что он указывает в правильном направлении, хотя это не совсем то, о чем я спрашивал в первую очередь.
@ansanes Я имею в виду именно то, что вы указали. Он не отвечает на то, что вы просили (после моего редактирования, которое прояснило, что вы хотите).
Похоже, uniq делает все, что вам нужно.
-д, --повторил
печатать только повторяющиеся строки, по одной для каждой группы
-s, --skip-chars=N
избегать сравнения первых N символов
Так что это должно работать для вас:
uniq --skip-chars=17 -d /tmp/file.txt
Проверено на моей машине:
$ cat in.txt
2020-01-01 05:05 text1
2020-01-01 05:07 error
2020-01-01 05:15 error
2020-01-01 05:25 error
2020-01-01 05:45 text2
$ uniq --skip-chars=17 -d in.txt
2020-01-01 05:07 error
Вы можете получить номер первой строки, где он встречается, выполнив cat -n |
и отрегулировав значение пропуска, например. cat -n in.txt | uniq -ds 25
.
Это работает. Я использую его так: [[ $(uniq --skip-chars=28 -c -d /var/log/file.log | grep "error" | awk '$1 > 2' | wc) > 2 ]]
чтобы указать текст, который я ищу, и количество последовательных строк с совпадением.
Хорошо, теперь вы ввели кучу других требований. uniq -c -d
доставит вам только дубликаты со счетом; количество всегда будет не менее 2, но вы ищете те, где оно не менее 3. И затем вы считаете их и ищете файлы, в которых это происходит более двух раз?
FWIW, вам никогда не нужно делать grep | awk
. Вместо grep "error" | awk '$1 > 2'
можно написать awk '/error/ && $1 > 2'
. Но вся эта конструкция кажется излишне окольной.
Вы тоже не хотите [[ ... > 2 ]]
; это делает лексическое сравнение, что означает, что [[ 12 > 2 ]]
ложно, потому что 1 сортируется раньше 2. Чтобы выполнить числовое сравнение, вам нужно ((
...))
вместо [[
...]]
.
Спасибо за ваши предложения. Идея состоит в том, чтобы проверить, содержит ли файл «текст» в «n» последовательных строках, где text и n являются параметрами.
Это не делает то, о чем просил ОП, I'd like to have an exit value 0 for match and 1 for not match
, поэтому я удивлен, что это принятый ответ.
@ansanes Что касается вашего комментария - не делайте этого, так как это запутанно и хрупко по сравнению, например, с одной командой awk. Задайте новый вопрос, чтобы получить помощь, так как этот вопрос не был хорошей отправной точкой для того, что вы действительно пытаетесь сделать.
Один в awk для проверки двух или более последовательных строк, что для меня означает выход сразу после двух последовательных строк:
$ awk -v s = "word" '{ # search word as a parameter
if ($3==p&&$3==s) # if third word is the same as from previous round
exit ec=1 # and the same as the search word, exit right away
else
p=$3 # else just store the last word for next round
}
END { # in the end
exit !ec # flip the error code and exit
}' file
Попробуй это:
$ awk -v s=error '{if ($3==p&&$3==s)exit ec=1;else p=$3}END{exit !ec}' matching
$ echo $?
1
$ awk -v s=error '{if ($3==p&&$3==s)exit ec=1;else p=$3}END{exit !ec}' nonmatching
$ echo $?
0
В приведенном выше примере данных учитываются только третьи слова (или поля, разделенные пробелами). Если вы ищете строку длиннее слова, рассмотрите возможность замены $3
на substr($0,n)
, где n==18
в вашем образце (начальная точка строки после части даты и времени):
$ awk -v s=error '{
if (substr($0,18)==p&&substr($0,18)==s)
exit ec=1
else
p=substr($0,18)
}
END {
exit !ec
}' file
Вы знаете, как присвоить значение поля переменной в
awk
?