2013-04-29 50 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模塊更加複雜,但對平臺無關的文件名和路徑操作有很好的支持。