I run diff
with the -p
option so the output will include the name of the function where each change occurred. Is there an analogous option for
Here is an imperfect solution. It has the following flaws:
ctags
I called my script `cgrep.sh', which has the following syntax:
cgrep.sh search-term files...
Cgrep.sh works by relying on ctags
to produce a list of search patterns for function headers. We can then search for both the function headers and the search term.
Without further ado, here is 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
Actually "grep -p" has been a fixture in AIX for the last two decades from what I can recall. It is out there, and it's just a matter of porting the behaviour over in fresh code.
It's crude, though, and may need help to know that blank lines within a function don't count.
I wrote a script to grep C files and show the C function names and signature along with the results. Based on 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 #####
Enjoy. :)