我有大量的脚本需要修改。每个脚本可能调用 5 到 10 个其他脚本,而这些脚本又可能调用其他几个脚本,谁知道这个兔子洞有多深。
有没有办法获取特定脚本调用的脚本列表?
我在想这样的事情:
/home/root/ $ showscripts mytargetscript
输出将类似于:
/home/root/mytargetscript
/home/root/asubscript
/home/root/bsubscript
...
如果我冒犯了 Unix 众神,我会提前进行忏悔。我在 dba.se 上回答了很多问题。
答案1
没有通用的解决方案,因为脚本可以使用无数种方式来调用其他脚本。您可以执行grep
可能适用于您的脚本但不适用于一般情况的操作。
这会调用哪些脚本?
$(find / -executable -name "*.sh" -print0 | shuf -z -n 1)
如果您能够实际运行这些脚本,您可以通过两种方式跟踪它们。
set -x
将使您的脚本以扩展形式打印它执行的每个命令。然后,您可以检查这些命令中是否有运行脚本的命令。
strace -ff
过度杀戮,但strace
为您提供了进程所做的一切,并且通过选项,-ff
它也几乎沿着兔子洞走到了尽头。我说几乎是因为有很多方法可以绕过它。它遵循守护进程吗?
Grep strace
for 调用open()
或exec*()
并过滤它以查找脚本文件,您可能会接近完整的图片[对于您所做的脚本的一次运行 - 不包括仅在其他条件下调用的脚本]。
$ strace -ff ./testscript.sh |& grep 'open.*\.sh"'
open("./testscript.sh", O_RDONLY) = 3
[pid 24486] open("./CD-DVD Image erstellen.sh", O_RDONLY) = 3
因此,您可以创造性地提出解决方案,但不要指望一个适合所有情况的解决方案。
答案2
假设所有脚本都位于同一目录中,它们的名称中没有制表符或换行符,并且您在文件中拥有“有趣”脚本的列表scripts.txt
,每行一个,并且还假设您的 shell 可以进行<(...)
进程替换:
#! /bin/sh
while read -r s; do
fgrep -o -w -f <(fgrep -v -w "$s" scripts.txt) "$s" /dev/null | \
sort -u | \
tr : '\t' >>calls.txt
done <scripts.txt
这会构建一个文件calls.txt
。该文件由制表符分隔的对组成script1 script2
,描述“script1
调用script2
”关系。
您需要输入calls.txt
以下 Perl 脚本:
#!/usr/bin/env perl
use v5.10;
use strict;
use warnings;
use Graph::Directed;
my $g = Graph::Directed->new;
while (<>) {
chomp;
$g->add_edge( split /\t/ );
}
for ( sort $g->vertices ) {
say "$_: " . join(', ', sort $g->all_successors($_));
}
该脚本根据调用关系构建有向图,然后打印所有顶点的后继(即脚本)。
当然,该脚本需要Perl模块Graph
。假设您有该cpanm
脚本,您可以通过运行来安装所需的模块cpanm Graph
。
答案3
给定一个脚本
$ cat the_script
ls
/bin/ls
touch ./xx.xx
ls
/bin/ls
ls
ls
/bin/ls
ls
/bin/ls(tabs)
然后
$ cat the_script | grep ^[[:space:]]*\t*\/
产生
/bin/ls
/bin/ls
/bin/ls
/bin/ls
因此,如果这些调用从行的乞求处开始(允许空格或制表符),则会显示这些调用
您还可以识别“执行此操作的文件列表”的可执行文件,而不是维护硬编码列表,例如
$ find -type f -perm /u=x,g=x,o=x
./test.sh
./the_script
你可以用管道
答案4
一种可能的方法(未经测试)如下。
摘要:你有很多 shell 脚本,它们都互相调用。你想知道这些脚本之间的调用关系是什么。
我们假设这些脚本都是作为单独的程序调用的,因为 Gilles 告诉我,在这种情况下,被调用的脚本是调用脚本的子进程。
然后在每个脚本中添加一些代码,将进程 ID、父进程 ID 和脚本名称等信息写入数据库。该代码对于所有脚本来说都是通用的。然后在运行脚本后对数据库信息进行后处理,以确定脚本之间的调用关系。