【发布时间】:2011-09-02 07:15:52
【问题描述】:
我使用-p 选项运行diff,因此输出将包括发生每次更改的函数的名称。 grep 有类似的选项吗?如果没有,我还可以使用什么其他命令?
而不是 -B 来显示紧接在匹配之前的固定数量的上下文行,我希望匹配之前只有一行具有最新的函数签名,不管它后面有多行文件。如果我要查找的选项是 -p,则输出可能如下所示,例如:
【问题讨论】:
我使用-p 选项运行diff,因此输出将包括发生每次更改的函数的名称。 grep 有类似的选项吗?如果没有,我还可以使用什么其他命令?
而不是 -B 来显示紧接在匹配之前的固定数量的上下文行,我希望匹配之前只有一行具有最新的函数签名,不管它后面有多行文件。如果我要查找的选项是 -p,则输出可能如下所示,例如:
【问题讨论】:
假设您正在搜索 foobar:
grep -e "^\w.*[(]" -e foobar *.h *.cpp | grep -B 1 foobar
greps 用于所有函数和所有 foobar,然后 greps 仅用于 foobar 和前面的行 - 这将只是 foobars 和包含的函数。
在 Windows 版本的 cygwin 上测试
【讨论】:
grep -e ^\\w.*\( -e 'return' foo.c | grep -B 1 'return'
给你:
git grep --no-index -n -p 'return'
你只需要 git。被搜索的文件不需要是 git repo 的一部分。
但如果是,请忽略 --no-index 并立即获得速度提升!
【讨论】:
git,因此该解决方案适用于大多数人。
据我回忆,实际上“grep -p”在过去的二十年里一直是 AIX 中的固定装置。它就在那里,只是将行为移植到新代码中的问题。
不过,这很粗略,可能需要帮助才能知道函数中的空白行不算数。
【讨论】:
与大多数文本处理操作一样,使用 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:]]/) 开头的行。如果这不是您编写代码的方式,只需调整以适应。
【讨论】:
// 中排除re?
//return bla bla bla
您可以编写一个脚本,将grep -vs 写入一个临时文件,然后将diff -ps 与原始文件一起写入。这样diff 将找到grep 删除的行(即您想要的行),并且您将获得完全相同的函数匹配。
【讨论】:
我编写了一个脚本来 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 "$@" | \
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 #####
享受。 :)
【讨论】:
这是一个不完美的解决方案。它有以下缺陷:
ctags 的工具
我将我的脚本命名为“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 "$@" # 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 "$@" # Grep both headers and search term
rm tags tags.bak # Clean up
【讨论】:
很遗憾,没有。此功能在grep 中不存在,ack 中也不存在(这是对grep 的替换改进)。
不过,我真的希望这存在。它会派上用场的。 Someone did take a shot at implementing it a while back,但看起来他们的补丁从未被接受(或者甚至从未在网上发布过,奇怪的是)。您可以尝试给他发电子邮件,看看他是否还有代码,并且仍然希望获得将 C 函数显示到 grep 的选项。
您可以编写一个正则表达式来匹配一个 C 函数,但我敢打赌那将是一个正则表达式的怪物。
【讨论】:
| 函数签名。会有一些额外的噪音,但你会得到你想要的。
diff 使用的那个更好——甚至 ^\w.*\( 似乎做得很好.但是在不将其限制在“真实”匹配的上下文的情况下应用它会产生很多的额外噪音——我当前项目的一个子目录中只有 1300 多个匹配。
GNU grep 中没有这样的功能,虽然过去一直是discussed。
但是,如果您的代码在 git 的控制之下,git grep 有一个选项 -p 可以做到这一点。
【讨论】:
grep -p 时所想的。我现在正在使用 Mercurial,但我可能会在下一个项目中尝试 Git。谢谢。