1.目标和方案
目标:给定一个类名称,在一个目录下查找该类所有的直接和间接子类,并查找该类和其子类被调用的地方。
方案:第一步:遍历目录查找该类的所有子类。例如类名称为A,此时需要查找extends A的所有地方,取出子类名称B,再查询extends B出现的地方,获取到B的子类......以此递归找到所有的A的直接和间接子类,并将类A和其所有的子类存入一个文件final_class_name.txt中。第二步:在指定目录下遍历final_class_name.txt中的类名称,查询类名称在目录下被调用的文件路径,并存入到final_result.txt文件中。
2.代码
shell脚本文件search_classname.sh代码如下,该脚本执行需要两个参数,第一个参数为目录,第二个参数为类名称。
#!/bin/bash
dir=$1
keyword=$2
#先删除搜索过程中会用到的两个文件final_class_name.txt和final_result.txt
rm -rf final_class_name.txt
rm -rf final_result.txt
#判断该路径是否存在,并且是目录,不存在输出提示
test ! -d $dir && echo -e "The $dir is not exist in your system.\n\n" && exit 0
echo -e "\n---------------start search $keyword and it's subclass.......\n"
#统计文件个数
file_count=0
#递归查看所有目录,即最深路径,不显示空行
file_list=`ls -R $dir 2> /dev/null | grep -v '^$'`
#step 1.找出所有和keyword相关的直接继承类和间接继承类,并存放在一个keywords文件中
echo $keyword >> final_class_name.txt
source class_function
search_all_class $1 $keyword
#step 2.遍历final_class_name.txt文件,查找这些相关类名所在的文件路径
#要遍历查询的类名文件
class_file=./final_class_name.txt
for line in $(cat $class_file)
do
#显示要查询的类名
#echo $line
#调用search_classname脚本,传入目录路径和要查询的类名称
source class_function
search_class_path $dir $line
done
echo -e "\nSearch class and it's subclass Finished!\n"
以上代码中调用的一个函数search_all_class和search_class_path存放在class_function文件中,class_function文件代码如下:
function search_all_class()
{
local dir=$1
local extends="extends "
local keyword=${extends}${2}
#判断该路径是否存在,并且是目录,不存在输出提示
test ! -d $dir && echo -e "The $dir is not exist in your system.\n\n" && exit 0
#统计文件个数
local file_count=0
#1递归查看所有目录,即最深路径,不显示空行
local file_list=`ls -R $dir 2> /dev/null | grep -v '^$'`
for file_name in $file_list
do
#临时文件变量temp,将ls -R即file_list中的文件名中所有匹配:后接一个或多个任意字符(.代表任意字符,*代表0个或多个$代表行尾结束符)全局替换为无
#简单的说,就是把file_name变量中的匹配:的行,将:后内容替换为空
temp=`echo $file_name | sed 's/:.*$//g'`
#如果临时文件变量temp是一个目录,而非文件,就将该目录赋值给cur_dir变量
if [ "$file_name" != "$temp" ]; then
local cur_dir=$temp
#echo "-"$cur_dir #临时显示,调试用
else
#用file命令查看文件真身是否为ASCII text类型
file_type=`file $cur_dir/$file_name | grep "text"`
if [ "$file_type" != "" ]; then
temp=`grep "$keyword" $cur_dir/$file_name 2> /dev/null`
if [ "$temp" != "" ]; then
#echo $cur_dir/$file_name
#文件个数加1
let file_count++
#echo "temp1="$temp
#删除extends开始的后边内容
temp=${temp%extends*}
#echo "temp2="$temp
#删除class 结尾的前边内容太,此时,只剩下类名称了
temp=${temp#*class }
#echo "temp3="$temp
#final_class_name.txt用于最终的整体查找
echo $temp >> final_class_name.txt
search_all_class $dir $temp
else
continue
fi
fi
fi
done
}
#$1为输入目录,$2为要查找的类名称
#找到所有的调用该类的地方,并将文件路径存储在final_result.txt中
function search_class_path()
{
dir=$1
keyword=$2
#判断该路径是否存在,并且是目录,不存在输出提示
test ! -d $dir && echo -e "The $dir is not exist in your system.\n\n" && exit 0
#统计文件个数
file_count=0
#递归查看所有目录,即最深路径,不显示空行
file_list=`ls -R $dir 2> /dev/null | grep -v '^$'`
for file_name in $file_list
do
#临时文件变量temp,将ls -R即file_list中的文件名中所有匹配:后接一个或多个任意字符(.代表任意字符,*代表0个或多个$代表行尾结束符)全局替换为无
#简单的说,就是把file_name变量中的匹配:的行,将:后内容替换为空
temp=`echo $file_name | sed 's/:.*$//g'`
#如果临时文件变量temp是一个目录,而非文件,就将该目录赋值给cur_dir变量
if [ "$file_name" != "$temp" ]; then
cur_dir=$temp
#echo "-"$cur_dir #临时显示,调试用
else
#echo "file_name=$file_name"
#用file命令查看文件真身是否为ASCII text类型
file_type=`file $cur_dir/$file_name | grep "text"`
if [ "$file_type" != "" ]; then
temp=`grep $keyword $cur_dir/$file_name 2> /dev/null`
if [ "$temp" != "" ]; then
echo $cur_dir/$file_name >> final_result.txt
#文件个数加1
let file_count++
fi
fi
fi
done
}
3.运行示例
执行脚本实例如下,第一个参数/home/zhuws/android8.1/j6-6a8.1/packages/apps/Bluetooth为需要查询的目录,第二个参数StateMachine为要查询的类名称。
./search_classname.sh /home/zhuws/android8.1/j6-6a8.1/packages/apps/Bluetooth StateMachine
执行完后,就会在同一个目录下看到下面两个文件,查看里边结果就可以了。
-rw-rw-r-- 1 zhuws zhuws 194 11月 15 16:28 final_class_name.txt
-rw-rw-r-- 1 zhuws zhuws 8565 11月 15 16:29 final_result.txt
4.一些总结
1)递归查询目录
要遍历目录之前,先要查到目录下所有的子目录和文件有哪些,于是想到了ls命令。使用ls -R folder可以将目录下所有的子目录和文件都列出来,相当于我们编程中的“递归”实现。这块网上也参考了网上一篇博客的代码,具体忘记链接地址了。
2)shell中递归函数使用
shell中递归函数这块我写的时候遇到了个大坑:下层函数调用后把上层函数里的变量值给修改了,导致不能正常递归遍历。后来查找资料,终于知道递归函数中所有变量都要前边加上local,表示其范围是在当前函数内。参考了这篇文章https://www.crifan.com/linux_shell_recursive_function_variable_be_changed/
3)截取字符串
通过extends A查到了public class B extends A这样一行后,需要将里边的B截取出来。字符串接截取可以参考这个链接:https://www.cnblogs.com/zwgblog/p/6031256.html
------finish--------