生成Shell脚本调用树

9
我被分配了一个由几十个(可能超过100个,我没有数过)bash脚本组成的项目。大多数脚本至少调用另一个脚本中的一个函数。我想得到一个等效的调用图,其中节点是脚本而不是函数。
有没有现成的软件可以做到这一点?
如果没有,有没有聪明的想法来实现这个功能?
我能想到的最好的计划是枚举脚本并检查基名是否唯一(它们跨越多个目录)。如果有重复的基名,则会出现问题,因为脚本路径通常保存在变量名中,因此您可能无法消除歧义。如果它们是唯一的,则在脚本中搜索名称并使用这些结果构建图形。使用一些工具(有什么建议吗?)来可视化图形。
有什么建议吗?

Graphviz是一个很好的可视化工具,但在将其传递给Graphviz之前,您需要以某种方式预处理基本名称的输出。您可以尝试使用bashdb来调试脚本。 - vissi
我现在经常做的是在我创建的每个脚本开头添加一个“Depends:”注释。这可能不可行,如果要在更大范围内进行,但这是一个非常简单的约定,为我服务得很好。 - tripleee
3个回答

3
这是我最终采用的方法(免责声明:此方法有些粗糙,如果您要长期使用,请进行清理)......

假设: - 当前目录包含所有相关脚本/二进制文件。 - 用于构建图形的文件放在名为call_graph的子目录中。

创建了名为call_graph/make_tgf.sh的脚本:

#!/bin/bash
# Run from dir with scripts and subdir call_graph
# Parameters:
# $1 = sources (default is call_graph/sources.txt)
# $2 = targets (default is call_graph/targets.txt)

SOURCES=$1
if [ "$SOURCES" == "" ]; then SOURCES=call_graph/sources.txt; fi
TARGETS=$2
if [ "$TARGETS" == "" ]; then TARGETS=call_graph/targets.txt; fi

if [ ! -d call_graph ]; then echo "Run from parent dir of call_graph" >&2; exit 1; fi
(
#  cat call_graph/targets.txt
  for file in `cat $SOURCES `
  do
    for target in `grep -v -E '^ *#' $file | grep -o -F -w -f $TARGETS | grep -v -w $file | sort | uniq`
    do echo $file $target
    done
  done
)

然后,我运行了以下命令(最终我选择了仅脚本版本):

cat /dev/null | tee call_graph/sources.txt > call_graph/targets.txt
for file in *
do
  if [ -d "$file" ]; then continue; fi
  echo $file >> call_graph/targets.txt
  if file $file | grep text >/dev/null; then echo $file >> call_graph/sources.txt; fi
done

# For scripts only:
bash call_graph/make_tgf.sh call_graph/sources.txt call_graph/sources.txt > call_graph/scripts.tgf

# For scripts + binaries (binaries will be leaf nodes):
bash call_graph/make_tgf.sh > call_graph/scripts_and_bin.tgf

我随后在 yEd 中打开生成的 tgf 文件,并让 yEd 进行布局(Layout -> Hierarchical)。我将其保存为 graphml,以便手动编辑的文件与自动生成的文件分离。
我发现有些节点对图表没有帮助,例如被频繁调用的实用程序脚本/二进制文件。因此,我从源/目标文件中删除了这些节点,并根据需要重新生成,直到我满意为止。
希望这能帮助到某些人...

我无法设置yEd。是否有其他方法可以查看TGF文件? - user13107

3

使用你自己的实现包装Shell本身,记录调用你包装的人并执行原始Shell。

是的,您必须启动脚本以确定实际使用的脚本。否则,您需要具有与Shell引擎本身相同的知识的工具来支持整个变量扩展、路径等——我从未听说过这样的工具。

为了可视化调用图,请使用GraphViz的点格式。


为聪明才智点赞。也许我不够有创意,但从未想过包装外壳。不幸的是,对我来说运行所有这些程序并不实际;我甚至不知道它们大多数是做什么的或哪些是重要的。我同意变量扩展和路径问题很难,但我必须根据代码来完成它,否则只能放弃。 - Michael Rusch
1
如果您正在考虑新的实现,请考虑使用原始shell源代码(开源?)并在其中添加您的逻辑。这样,您将获得更少的解析器。尝试禁用除shell脚本本身之外的所有exec()调用,并记录您要查找的内容。因为我从未听说过这样的项目,所以我很想听听您的进展。祝你好运。 - Raphael Bossek

0
在每个shell脚本的#!行之后插入一行,记录时间戳、脚本的完整路径名和参数列表。
随着时间的推移,您可以挖掘此日志以识别可能的候选项,即两行非常接近的日志很有可能是第一个脚本调用第二个脚本。
这也使您能够专注于仍然实际使用的脚本。
您可以使用ed脚本。
1a
log blah blah blah
.
wq

然后像这样运行:

find / -perm +x -exec ed {} <edscript

确保您使用-print而不是exec子句测试find命令。/可能不是您想要使用的路径。如果必须包括bin目录,则可能需要切换到grep以识别要包括的路径名,然后当您有一个包含正确名称的文件时,请改用xargs而不是find来运行脚本。


哦,你还需要编写一个日志脚本或者最好是一个shell别名/函数。 - Michael Dillon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接