如果一个.cpp或.h文件包含#includes(例如#include“ready.h”),我需要创建一个文本文件,其中包含这些文件名。由于ready.h可能有自己的#includes,因此必须递归调用。不知道如何做到这一点。如何使用Perl,awk或sed进行递归调用?
回答
在Perl中,递归是直截了当:
sub factorial
{
my $n = shift;
if($n <= 1)
{ return 1; }
else
{ return $n * factorial($n - 1); }
}
print factorial 7; # prints 7 * 6 * 5 * 4 * 3 * 2 * 1
随口说说,我能想到的只有两个需要照顾的事情:
- 在Perl中,变量是由默认的全局,因此,静态默认。既然你不想让一个函数调用的变量践踏另一个变量,你需要确保本地化你的变量,例如通过使用
my
。 - 原型和递归有一些限制。如果要使用原型(例如
sub factorial($)
而不是sub factorial
),则需要在之前提供原型的函数定义,以便它可以在函数体内使用。 (或者,您也可以使用&
当你调用递归函数;这将阻止原型被应用。)
并不完全清楚自己想要的显示是什么样子,但基本会被称为脚本follow_includes.pl:
#!/usr/bin/perl -w
while(<>) {
if(/\#include "(\S+)\"/) {
print STDOUT $1 . "\n";
system("./follow_includes.pl $1");
}
}
运行它想:
% follow_includes.pl somefile.cpp
如果你想隐藏任何重复的包括运行它想:
% follow_includes.pl somefile.cpp | sort -u
通常你会想要某种树形打印。
谢谢,这个作品很棒。 – Exeter 2013-04-29 02:35:43
如果你喜欢它,请选择我的答案:) – OneSolitaryNoob 2013-07-29 06:13:40
@OneSolitaryNoob的解决方案可能会正常工作,但有一个问题:对于每个递归,它启动另一个进程,这是非常浪费的。我们可以使用子例程来更高效地完成这个任务。假设所有的头文件在工作目录:
sub collect_recursive_includes {
# Unpack parameter from subroutine
my ($filename, $seen) = @_;
# Open the file to lexically scoped filehandle
# In your script, you'll probably have to transform $filename to correct path
open my $fh, "<", $filename or do {
# On failure: Print a warning, and return. I.e. go on with next include
warn "Can't open $filename: $!";
return;
};
# Loop through each line, recursing as needed
LINE: while(<$fh>) {
if (/^\s*#include\s+"([^"]+)"/) {
my $include = $1;
# you should probably normalize $include before testing if you've seen it
next LINE if $seen->{$include}; # skip seen includes
$seen->{$include} = 1;
collect_recursive_includes($include, $seen);
}
}
}
这个子程序记住它已经看到的文件,避免了递归有一次,每个文件只到过一次。
在顶层,你需要提供一个hashref作为第二个参数,将控制所有的文件名作为关键字子运行后:
my %seen = ($start_filename => 1);
collect_recursive_includes($start_filename, \%seen);
my @files = sort keys %seen;
# output @files, e.g. print "$_\n" for @files;
我在代码中暗示的意见,你会probabably必须规范文件名。例如,考虑起始文件名为./foo/bar/baz.h
,其指向qux.h
。那么我们想要缓存的实际文件名是./foo/bar/qux.h
,而不是./qux.h
。 Cwd
模块可以帮助您找到您当前的位置,并相对于绝对路径进行转换。 File::Spec
模块更加复杂,但对平台无关的文件名和路径操作有很好的支持。
- 1. 如何使用SED/AWK/perl的
- 2. 使用SED或AWK
- 3. 递归使用SED
- 4. 如何用AWK或sed或Perl做这样的替换?
- 5. 使用sed或awk更新一行
- 6. 如何使用变量来使用sed或awk进行搜索和替换?
- 7. 如何使用sed或awk去掉一组行或块?
- 8. 如何使用awk或sed打印两行相邻的行?
- 9. 使用sed或awk进行文本提取
- 10. 使用SED或AWK进行码型提取
- 11. 使用sed或awk进行xyz坐标操作
- 12. 使用sed/awk/
- 13. 使用awk,sed
- 14. 如何使用递归缩进行?
- 15. 用AWK递归调用失败?
- 16. 如何使用SQL进行递归调用?
- 17. 使用AWK或Perl转置
- 18. 如何用awk进行多次传递?
- 19. 使用awk命令进行递归搜索
- 20. 如何使用perl像sed?
- 21. 加:用awk或者sed
- 22. 如何使用awk,perl或sed从LiveHTTPHeaders输出中删除响应?
- 23. sed或awk多行替换
- 24. 使用Sed/AWK/Perl从块中提取第K行
- 25. 如何使用grep或awk或sed ..根据模式打印值?
- 26. 用sed或awk对输出数据进行分组
- 27. 重命名使用awk或者sed
- 28. 使用修改文件awk或者sed
- 29. 使用SED或AWK替换文本零
- 30. 使用awk或sed排列列?
由于* ready.h *可能有自己的'#include',所以调用必须以递归方式**行为,这意味着您可以在过程中简单地堆叠数据。 – Rubens 2013-04-29 00:34:24
我不知道该怎么做 – Exeter 2013-04-29 00:37:15
一个简单的方法是维护一个列表,在其中添加你找到的条目,并循环考虑条件为'while(list not empty);做$(与列表的最后一个元素的东西); $(在列表末尾插入新元素); ...; done'。但是,如果你试图生成依赖树,我不知道如何,但是为了这个目的必须有一些已经创造出来的东西。 – Rubens 2013-04-29 00:52:25