假设我在一个目录中有一个程序P11;它调用另外 2 个程序 P21 和 P22。 P21 和 P22 分别调用 2-2 个其他程序。
IE
Level-1: |--------------P11-------------|
Level-2: |--------P21----------| |----------P22----------|...
Level-3: P31 P32 P33 P34...
. . . .
. . . .
就像一个树形图,有 100 个级别,每个级别有 100 个节点,总共有 1000 个节点。
我需要一个 shell 脚本来给我这样的树结构。
例如shell脚本程序是-' MYPGM.sh
'。
它在运行时需要一个参数。我们可以在此处提供程序名称,例如 P11。并且脚本给出了这样一个树形图。
输出结构不必采用上面给出的类似图表的格式。我们可以使用索引或您建议的任何内容。例如输出文件可以是 –
1. P11
1.1 P21 - 1.2 P22
1.1.1 P31 - 1.1.2 P32 – 1.2.1 P33 – 1.2.2 P34
即每个程序的索引=它的父程序的索引+“。” + 它在该父程序中的序列号。
事实是 - 级别数和每个级别中的节点数未知,但非常大。
用于调用另一个程序的程序语法如下:
呼叫“CHILD_PGM_NAME”
在给定程序中查找子程序的命令如下:
egrep '^CALL| CALL ' $pgm_name > call_output | cut -d'"' -f2 call_output > call_output_cut | cat call_output_cut
我陷入了困境 - 如何循环此命令以获得上述输出文件。
有人可以提出建议吗?
答案1
对于这个问题,我建议 awk、python、perl 或类似的。下面的起点是一个 perl 脚本,如果尚未访问,它将对“程序”进行排队。输出采用图形可视化形式(如@derobert 建议)。
#!/usr/bin/perl
print "digraph{\n rankdir=LR \n";
my %visited = ();
while(<>){ # for all lines in all files in @ARGV
$visited{$ARGV}=1; # $ARGV = current file
if(/\bCALL\s*"(.+?)"/) {
print "$ARGV -> $1;\n" ;
push @ARGV,$1 unless $visited{$1};
$visited{$1}=1;
}
}
print "}\n";
用法:
perl calltree p1 > x.dot
dot -Tpdf -o x.pdf x.dot
或者(谢谢@derobert)
dot -Tx11 x.dot
立即查看图表。
正如@muru 指出的,通常这不是树。
答案2
@JJoao 的方法绝对是最适合使用的方法。但是,我编写了这个非常低效的脚本,可能会引起兴趣:
#! /bin/bash
SRC_DIR="/path/to/programs"
TMP_DIR="/tmp/tree"
declare -A FUNCS
iterate ()
(
program="$1"
FUNCS["$program"]="$TMP_DIR/$program"
mkdir "${FUNCS[$program]}"
grep -Po '(?<=\bCALL ")[^"]*' "$SRC_DIR/$program" | while read child
do
if [[ -n ${FUNCS[$child]} ]]
then
ln -s "${FUNCS[$child]}" "${FUNCS[$program]}/$child"
else
iterate "$child"
fi
done
)
[[ -n $1 ]] && { iterate "$1"; tree -dlo call-graph.html -H "$TMP_DIR" "$TMP_DIR"; }
rm -rf "$TMP_DIR"
它创建一个目录树,每个目录代表一个函数,包含指向它调用的其他目录/函数的符号链接。该tree
程序天生擅长绘制树(该-l
选项遵循符号链接,除非检测到递归,-o
生成 HTML 输出)。
当然,理想情况下,您应该查看适合您的语言的调用图生成器。这那么问题列出了一些 C 语言的版本,也许您能够找到支持您的语言的版本。