2013-04-29 51 views
0

如果一个.cpp或.h文件包含#includes(例如#include“ready.h”),我需要创建一个文本文件,其中包含这些文件名。由于ready.h可能有自己的#includes,因此必须递归调用。不知道如何做到这一点。如何使用Perl,awk或sed进行递归调用?

+0

由于* ready.h *可能有自己的'#include',所以调用必须以递归方式**行为,这意味着您可以在过程中简单地堆叠数据。 – Rubens 2013-04-29 00:34:24

+0

我不知道该怎么做 – Exeter 2013-04-29 00:37:15

+0

一个简单的方法是维护一个列表,在其中添加你找到的条目,并循环考虑条件为'while(list not empty);做$(与列表的最后一个元素的东西); $(在列表末尾插入新元素); ...; done'。但是,如果你试图生成依赖树,我不知道如何,但是为了这个目的必须有一些已经创造出来的东西。 – Rubens 2013-04-29 00:52:25

回答

0

在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),则需要在之前提供原型的函数定义,以便它可以在函数体内使用。 (或者,您也可以使用&当你调用递归函数;这将阻止原型被应用。)
0

并不完全清楚自己想要的显示是什么样子,但基本会被称为脚本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 

通常你会想要某种树形打印。

+0

谢谢,这个作品很棒。 – Exeter 2013-04-29 02:35:43

+0

如果你喜欢它,请选择我的答案:) – OneSolitaryNoob 2013-07-29 06:13:40

2

@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.hCwd模块可以帮助您找到您当前的位置,并相对于绝对路径进行转换。 File::Spec模块更加复杂,但对平台无关的文件名和路径操作有很好的支持。