如何使用Perl,awk或sed进行递归调用?

时间:2013-04-29 00:31:26

标签: perl sed awk grep

如果.cpp或.h文件有#includes(例如#include“ready.h”),我需要制作一个包含这些文件名的文本文件。由于ready.h可能有自己的#includes,因此必须以递归方式进行调用。不知道怎么做。

3 个答案:

答案 0 :(得分: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;

我在代码评论中暗示你可能需要规范化文件名。例如,考虑起始文件名./foo/bar/baz.h,其指向qux.h。那么我们不想递归到的实际文件名是./foo/bar/qux.h,而不是./qux.hCwd模块可以帮助您找到当前位置,并相对于绝对路径进行转换。 File::Spec模块要复杂得多,但对平台无关的文件名和路径操作有很好的支持。

答案 1 :(得分: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),那么你需要在函数定义之前提供原型,以便它可以在功能体。 (或者,当您递归调用函数时,可以使用&;这将阻止原型的应用。)

答案 2 :(得分: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

通常你会想要某种树形图。