Ответ 1
В GNU grep нет такой функции, хотя в прошлом она обсуждалась .
Однако, если ваш код находится под управлением git
, git grep
имеет опцию -p
, которая сделает это.
Я запускаю diff
с опцией -p
, поэтому на выходе будет указано имя функции, в которой произошло каждое изменение. Есть ли аналогичная опция для grep
? Если нет, какую другую команду я мог бы использовать вместо этого?
Вместо -B
, чтобы показать фиксированное количество строк контекста, которые непосредственно предшествуют совпадению, мне хотелось бы, чтобы для совпадения предшествовала только одна строка с самой последней сигнатурой функции, однако многие строки назад были в файл. Если параметр, который я ищу, был -p
, вывод может выглядеть так, например:
$ cat foo.c int func1(int x, int y) { return x + y; } int func2(int x, int y, int z) { int tmp = x + y; tmp *= z; return tmp; } $ grep -p -n -e 'return' foo.c 1-int func1(int x, int y) 3: return x + y; -- 5-int func2(int x, int y, int z) 9: return tmp;
В GNU grep нет такой функции, хотя в прошлом она обсуждалась .
Однако, если ваш код находится под управлением git
, git grep
имеет опцию -p
, которая сделает это.
Ну вот:
git grep --no-index -n -p 'return'
Тебе просто нужен мерзавец. Разыскиваемые файлы не обязательно должны быть частью git-репо. Но если они есть, то опустите --no-index
и получите мгновенный прирост скорости!
Предполагая, что вы ищете foobar:
grep -e "^\w.*[(]" -e foobar *.h *.cpp | grep -B 1 foobar
greps для всех функций и всех foobar, затем greps для просто foobar и предыдущих строк - которые будут только foobars и содержащими их функциями.
протестировано на Cygwin на Windows версии
К сожалению, нет. Эта функция не существует в grep
и не существует в ack
(которая улучшена с заменой grep
).
Я действительно желаю, чтобы это существовало. Это пригодится. Кто-то сделал попытку реализовать его некоторое время назад, но это не похоже, что их патч когда-либо принимался (или когда-либо даже размещался онлайн, странно). Вы можете попробовать отправить его по электронной почте и посмотреть, есть ли у него код и все еще хочет получить возможность показывать функции C в grep
.
Вы можете написать регулярное выражение для соответствия функции C, но я уверен, что это был бы один монстр регулярного выражения.
Я написал файл script для grep C и покажу имена и подпись функции C вместе с результатами. На основе ctags.
#!/bin/bash
#
# grep_c_code
#
# Grep C files and print the results along with the function name and signature.
# Requires: ctags, gawk, sed, bash, and you probably want grep too.
#
# Written by David Stav, December 19 2012.
#
# Released to the public domain.
#
if [ $# -lt 2 ]; then
echo "Usage: $0 <grep_cmd> <files/dirs...>" >&2
echo "" >&2
echo "Example:" >&2
echo " $0 'grep --color=always -n -e \"PATTERN\"' file1 file2 dir1 dir2 | less -R" >&2
exit 1
fi
GREP_CMD="$1"
shift
GAWK_SCRIPT="`
sed -n -e '/^##### START of gawk script #####$/,/^##### END of gawk script #####$/p' \"$0\" | \
sed -n -e '2,$ { $ D; p}'
`"
ctags -f - -R --sort=no -n --fields=+afikKmsSzt --extra=+fq "[email protected]" | \
gawk "$GAWK_SCRIPT" "$GREP_CMD" | \
bash
exit 0
##### START of gawk script #####
function parse_line(a)
{
a["tagname"] = $1;
a["filename"] = $2;
a["line_number"] = gensub(/^([0-9]+).*$/, "\\1", 1, $3);
if (a["line_number"] == $3)
{
a["line_number"] = "0";
}
a["kind"] = gensub(/^.*\tkind:([^\t]+).*$/, "\\1", 1, $0);
if (a["kind"] == $0)
{
a["kind"] = "unknown kind";
}
a["signature"] = gensub(/^.*\tsignature:(.*)$/, "\\1", 1, $0);
if (a["signature"] == $0)
{
a["signature"] = "";
}
}
function grep_section(a, next_line_number)
{
printf("\n");
printf("\n");
printf("\n");
printf("cat '%s' | \\\n", a["filename"]);
printf("sed -n -e '%s,%sp' | \\\n", a["line_number"], next_line_number);
printf("%s | \\\n", grep_cmd);
printf("sed -e '1 i \\\n");
printf("\\n\\n\\n--\\\n");
printf("[%s:%s]\\\n", a["filename"], a["line_number"]);
printf("<%s> %s%s\\\n", a["kind"], a["tagname"], a["signature"]);
printf("'\n");
}
BEGIN \
{
FS = "\t";
grep_cmd = ARGV[1];
ARGV[1] = ""
}
!/^!/ \
{
parse_line(next_line);
if (a["line_number"])
{
next_line_number = next_line["line_number"] - 1;
grep_section(a, next_line_number);
delete a;
}
for (key in next_line)
{
a[key] = next_line[key];
}
}
END \
{
if (a["line_number"])
{
next_line_number = "$";
grep_section(a, next_line_number);
}
}
##### END of gawk script #####
Enjoy.:)
Как и в большинстве операций обработки текста, это тривиально с awk:
$ awk -v re='return' '/^[[:alpha:]]/{f=FNR"-"$0} $0~re{printf "%s\n%d:%s\n--\n",f,FNR,$0; f="" }' file
1-int func1(int x, int y)
3: return x + y;
--
5-int func2(int x, int y, int z)
9: return tmp;
--
Вышеприведенное предполагает, что сигнатурой функции является любая строка, начинающаяся с буквы (/^ [[: alpha:]]/). Если это не так, как ваш код написан, просто подкорректируйте его.
Вот несовершенное решение. Он имеет следующие недостатки:
ctags
Я назвал свой script `cgrep.sh ', который имеет следующий синтаксис:
cgrep.sh search-term files...
Cgrep.sh работает, полагаясь на ctags
, чтобы создать список шаблонов поиска для заголовков функций. Затем мы можем искать как заголовки функций, так и поисковый запрос.
Без дальнейших церемоний, вот cgrep.sh:
#!/bin/sh
# Grep, which includes C function headers
# cgrep term files*
TERM=$1 # Save the search term
shift
ctags "[email protected]" # produces the tags file
sed -i.bak 's:^.*/^:^:;s:/$::' tags # Prepare the tags file for grep
# Original contents is backed up to tags.bak
grep -f tags -e $TERM "[email protected]" # Grep both headers and search term
rm tags tags.bak # Clean up
На самом деле "grep -p" был инструментом в AIX за последние два десятилетия из того, что я могу вспомнить. Это там, и это просто вопрос переноса поведения в новый код.
Однако это грубо, и может понадобиться помощь, чтобы знать, что пустые строки внутри функции не учитываются.
Вы можете написать script, который grep -v
во временный файл, а затем diff -p
, который будет с оригиналом. Таким образом, diff
найдет строки, удаленные grep
(т.е. Строки, которые вы хотите), и вы получите точно такую же функцию.