我想通过一个大项目的源代码来阅读C函数的原型。
我知道函数名称及其返回类型,并且其原型将在文件中定义*.h
。
我会用grep(1)
,但我希望能够读取多行原型,所以它被丢弃了。
所以我通常做的事情是:
- 项目:
glibc
- 返回类型:
int
- 函数名称:
cacheflush
syscall='cacheflush';
find glibc/ -name '*.h' \
|xargs sed -n "/^[a-z ]*int ${syscall}[ ]*(/,/^$/p";
但这会在我想要的行之后打印一些不需要的行:
$ find glibc/ -name '*.h' \
|xargs sed -n "/^[a-z ]*int ${syscall}[ ]*(/,/^$/p";
extern int cacheflush (void *__addr, int __nbytes, int __op) __THROW;
#endif
extern int cacheflush (void *__addr, const int __nbytes,
const int __op) __THROW;
#endif
extern int cacheflush (void *__addr, const int __nbytes, const int __op) __THROW;
#endif
extern int _flush_cache (char *__addr, const int __nbytes, const int __op) __THROW;
extern int cacheflush (void *__addr, const int __nbytes, const int __op) __THROW;
#endif
extern int _flush_cache (char *__addr, const int __nbytes, const int __op) __THROW;
我希望能够替换结束模式/^$/
-> /;/
,但是只有当函数原型跨越多行时它才有效。是否可以告诉sed(1)
结束模式可能与开始模式在同一行,因此输出如下?:
$ find glibc/ -name '*.h' | xargs sed magic;
extern int cacheflush (void *__addr, int __nbytes, int __op) __THROW;
extern int cacheflush (void *__addr, const int __nbytes,
const int __op) __THROW;
extern int cacheflush (void *__addr, const int __nbytes, const int __op) __THROW;
extern int cacheflush (void *__addr, const int __nbytes, const int __op) __THROW;
答案1
您可以使用pcregrep
的多行模式:
$ pcregrep --include='\.h$' -rM '(?s)^\s*(\w+\s+)*int cacheflush\s*\(.*?;' glibc
glibc/sysdeps/unix/sysv/linux/mips/sys/cachectl.h:extern int cacheflush (void *__addr, const int __nbytes, const int __op) __THROW;
glibc/sysdeps/unix/sysv/linux/csky/sys/cachectl.h:extern int cacheflush (void *__addr, const int __nbytes,
const int __op) __THROW;
glibc/sysdeps/unix/sysv/linux/nios2/sys/cachectl.h:extern int cacheflush (void *__addr, const int __nbytes, const int __op) __THROW;
通过 PCRE,您可以访问 Perl 的大多数高级正则表达式运算符。在这里,我们使用:
\w
,以及\s
单词和空白字符。(?s)
:使s
标志.
也能够匹配换行符。*?
: 的非贪婪版本*
。所以它匹配第一的的发生;
,而不是像贪婪版本那样最后一次发生。
有关详细信息,请参阅pcrepattern(3)
手册页。
答案2
不需要调用sed
两次,只需在输入范围之前检查开始/结束是否恰好在同一行。
$ find glibc/ -name '*.h' \
|xargs sed \
-e "/${pattern}.*;\$/b" \
-e "/${pattern}/,/;\$/p" \
-e 'd' ;
find
请注意,如果您限制该实用程序仅查找常规文件,那就太好了,否则在sed
对名称以 a 结尾的目录进行操作时,您可能会看到警告.h
答案3
我想出了一个(丑陋的)解决方案:
读取从
/begin/
到/^$/
(空行),重复该模式的第一行,以便sed(1)
可以在后续步骤中对其采取行动。使用
sed(1)
查找从/begin/
到/end/
。在此步骤保留空行以便能够使用uniq(1)
在下一步中正确删除我们在步骤 1 中重复的行。使用
uniq(1)
删除重复的行。
$ syscall=cacheflush;
$ return=int;
$ pattern="^[a-z ]*${return} ${syscall}[ ]*(";
$ find glibc/ -name '*.h' \
|xargs sed -n -e "/${pattern}/p" -e "/${pattern}/,/^$/p" \
|sed -n -e "/${pattern}/,/;/p" -e '/^$/p' \
|uniq;
extern int cacheflush (void *__addr, int __nbytes, int __op) __THROW;
extern int cacheflush (void *__addr, const int __nbytes,
const int __op) __THROW;
extern int cacheflush (void *__addr, const int __nbytes, const int __op) __THROW;
extern int cacheflush (void *__addr, const int __nbytes, const int __op) __THROW;
请提供一个更简单的解决方案:)