#grep #matching
#grep #сопоставление
Вопрос:
Если я выполню grep -C 1 match
следующий файл:
a
b
match1
c
d
e
match2
f
match3
g
Я получаю следующий вывод:
b
match1
c
--
e
match2
f
match3
g
Как вы можете видеть, поскольку контекст вокруг смежных совпадений «match2» и «match3» перекрывается, они объединяются. Однако я бы предпочел получать одно описание контекста для каждого совпадения, возможно, дублируя строки из входных данных в отчете о контексте. В этом случае я хотел бы, чтобы:
b
match1
c
--
e
match2
f
--
f
match3
g
Какой был бы наилучший способ добиться этого? Я бы предпочел решения, которые являются достаточно общими, чтобы их можно было легко адаптировать к другим grep
параметрам (разные значения для -A
, -B
-C
или совершенно разные флаги). В идеале, я надеялся, что есть умный способ сделать это только с grep
….
Ответ №1:
Я не думаю, что это возможно сделать, используя обычный grep.
приведенная ниже конструкция sed работает в некоторой степени, теперь мне нужно только выяснить, как добавить разделитель «—«
$ sed -n -e '/match/{x;1!p;g;$!N;p;D;}' -e h log
b
match1
c
e
match2
f
f
match3
g
Комментарии:
1. Это результат, который он уже получает; он хочет чего-то другого. Пожалуйста, прочитайте вопрос, прежде чем отвечать.
2. Это не идеально, потому что вам пришлось бы изменить выражение sed неочевидным способом, если бы вы хотели иметь разные значения -C, или -A, или -B … не говоря уже об использовании других
grep
опций.3. @a3_nm это разумное требование для решения, но оно не указано в исходном вопросе. Я бы посоветовал вам отредактировать ваш вопрос, добавив его.
4. Все, что вам действительно нужно, это декоратор для вывода. Вы знаете токен, который ищете — этой информации достаточно, чтобы дублировать строки, где токен повторяется, вставляя —. Я думаю, что это
sed
решение приведет вас в правильном направлении. Вы могли бы составить сценарий оболочки, который просто вызывает grep с его идеей, передавая произвольные аргументы и выполняя ваш вопрос.
Ответ №2:
Я не думаю, что это возможно с использованием обычного grep.
Вы когда-нибудь использовали Python? На мой взгляд, это идеальный язык для таких задач (этот фрагмент кода будет работать как для Python 2.7, так и для 3.x):
with open("your_file_name") as f:
lines = [line.rstrip() for line in f.readlines()]
for num, line in enumerate(lines):
if "match" in line:
if num > 0:
print(lines[num - 1])
print(line)
if num < len(lines) - 1:
print(lines[num 1])
if num < len(lines) - 2:
print("--")
Это дает мне:
b match1 c -- e match2 f -- f match3 g
Комментарии:
1. Это необходимо было бы адаптировать для более сложного использования
grep
. Кажется неудовлетворительным переопределять сопоставление упрощенным способом (grep
делает это очень умным способом) только из-за этой отсутствующей опции. В любом случае, спасибо за предложение!
Ответ №3:
Я бы предложил исправить grep вместо того, чтобы обходить его. В GNU grep 2.9 в src/main.cpp:
933 /* We print the SEP_STR_GROUP separator only if our output is
934 discontiguous from the last output in the file. */
935 if ((out_before || out_after) amp;amp; used amp;amp; p != lastout amp;amp; group_separator)
936 {
937 PR_SGR_START_IF(sep_color);
938 fputs (group_separator, stdout);
939 PR_SGR_END_IF(sep_color);
940 fputc('n', stdout);
941 }
942
Здесь было бы достаточно простого дополнительного флага.
Редактировать: Ну, черт возьми, это, конечно, не так просто, поскольку grep не будет воспроизводить контекст, просто добавьте еще несколько разделителей. Из-за линейности grep весь патч, вероятно, не так прост. Тем не менее, если у вас есть веские аргументы в пользу исправления, оно того стоит.
Ответ №4:
Это не представляется возможным с grep или GNU grep. Однако это возможно с помощью стандартных инструментов POSIX и хорошей оболочки, такой как bash, в качестве рычага для получения желаемого результата.
Примечание: ни python, ни perl не должны быть необходимы для решения. В худшем случае используйте awk или sed.
Одно из решений, которое я быстро прототипировал, выглядит примерно так (оно действительно связано с накладными расходами на повторное чтение файла, и это решение зависит от того, приемлемы ли эти накладные расходы, а ответом является использование в исходном вопросе значения -1 в качестве фиксированного количества строк контекста, что позволяет просто использовать head amp; tail) :
$ OIFS="$IFS"; lines=`grep -n match greptext.txt | /bin/cut -f1 -d:`;
for l in $lines;
do IFS=""; match=`/bin/tail -n $(($l-1)) greptext.txt | /bin/head -3`;
echo $match; echo "---";
done; IFS="$OIFS"
С этим может быть связан некоторый угловой регистр, и это сбрасывает IFS, когда, возможно, в этом нет необходимости, хотя это подсказка для попытки использовать возможности POSIX shell amp; tools, а не высокоуровневый интерпретатор для получения желаемого результата.
Мнение: Все хорошие операционные системы имеют: grep, awk, sed, tr, cut, head, tail, more, less, vi в качестве встроенных модулей. В лучших операционных системах они находятся в /bin.