• 浅析Android设备中grep命令处理流程


    2017-04-18

     

    概述

        在TV开发板中,可以在串口中直接使用grep命令。这是因为在/system/bin/下有一个'grep'链接。这个链接指向'/system/bin/toolbox'程序。
    【图1】
    当在串口中键入:
    grep um
    命令时也就等效于键入
    toolbox grep um
     
      toolbox程序是Android系统中携带的工具​之一。其源码位于:
    ./system/core/toolbox/
    

    【图2】

      由上图2可以看到该目录下包含有大多数常用命令的源代码。toolbox程序的意义就是将这些零散的程序的入口进行打包。根据用户在串口中键入的toolbox命令后面的参数不同来选择执行对应的程序。例如,当键入上述的 ‘toolbox grep um’命令时,系统会先将命令的参数通过toolbox的进行解析,最终调用grep.c中的grep_main()函数进行二次解析。

     

    toolbox处理流程

    ./system/core/toolbox/toolbox.c
      该源码文件结构简单。内部仅两个函数:1.int main(int argc, char **argv);2.static int toolbox_main(int argc, char **argv);以及一个封装了各工具集入口函数的结构体数组tools[]。在这个源码中,最重要的就是这个静态结构体数组,其定义如下:
     1 #define TOOL(name) int name##_main(int, char**);
     2 #include "tools.h"
     3 #undef TOOL
     4 
     5 static struct 
     6 {
     7     const char *name;
     8     int (*func)(int, char**);
     9 } tools[] = {
    10     { "toolbox", toolbox_main },
    11 #define TOOL(name) { #name, name##_main },
    12 #include "tools.h"
    13 #undef TOOL
    14     { 0, 0 },
    15 };
      上述代码中,第1~3行的定义方式在我看来很新奇。但此处就简单把它理解成从tools.h中读取定义的信息,事实上也确实是这样。然后将这些字符串替换成相应的函数声明。tools.h是在本目录下的Android.mk文件中生成的。位于out目录下。
     
    其内容类型如下
    上述代码中,第1~3行是声明各工具的入口函数。第11~13行是使用这些外部函数。经过这样的声明以后,toolbox程序在运行时这个tools数组的实际值为
    1 tools[] = {
    2 { "toolbox", toolbox_main },
    3 {"ls", ls_main},
    4 .
    5 .
    6 .
    7 {"grep", grep_main},
    8 { 0, 0 },
    9 };
    而tools结构体的第二个参数又是一个函数指针,因此,tools数组中包含的就是toolbox目录下的所有工具的入口函数。估计这也是它会被取名为toolbox的原因吧。
     
      在运行toolbox程序的时候,首先会调用其主函数main()。在这个主函数main()中主要就是取跟在toolbox后面的参数来决定到底是要执行哪一个工具程序。如当键入:toolbox ls。则toolbox.c的执行顺序为:1.main();2.toolbox_main();3.main();4.ls_main()
     

    grep

        grep程序的入口函数为:grep_main()
    1 ./system/core/toolbox/grep/grep.c
    在这个文件的首部,定义了多个标志位用于区分不同的参数响应。
    常用的功能及其执行参数都已标示了出来。
        在grep_main()函数中,首先判断了键入参数的首字符。 __progname[0]等效于argv[0][0]。一般我们使用grep命令时,都是键入grep -n "xx",这时,__progname[0]得到的值就是字符g。看注释貌似是说区分于二进制模式下的什么什么的。对于我们由串口键入命令来执行程序的情况,是统一解析成字符g的,故而不需要理会它的真实用意。
     
        然后会从环境变量中读取搜索环境。
    读取到环境变量后做了什么事,我们姑且不管。不过这个的结果一般都是(null),因此实际上会执行如下操作:
        接下来就是去解析命令后面跟的参数了。
    图【3】
    函数getopt_long()的含义可查询百度。
    不得不承认,这个函数的功能很强大。它能解析各种花式输入的参数。同时它还具有排序的功能,将花式输入的命令行重新排序成'grep'在前,参数在其中,需要筛选的字符串在末尾的顺序。具体功能验证如下系列图所示,getopt_long()的排序功能验证如下图7所示。


        总之,经过图3所示的while循环后,能够识别所有的参数。例如,识别-n参数的代码如下图所示。
        接下来就是筛减aargv中的参数。
    其中,optind是getopt_long()函数中的一个变量,它的值是键入的'grep'字符串以及所有的参数的数量。
     
        接下来是去读取需要过滤的字符串。
    当键入的参数中包含-e  -f 时,needpattern的值就是0,否则为1。
    由此处代码可知,grep命令仅能搜索一个参数。add_pattern()函数的实现如下图所示。
    【图4】
        此函数的代码也算简单。grep命令是在pattern[]数组中保存要过滤的字符串的(pattern是一个二维数组)。而这个数组又被设计成“自动扩容”的形式。上图4所示代码中第230行的条件块就是一个自动扩容的处理流程了。grep_realloc()函数的定义位于
    1 ./system/core/toolbox/grep/util.c
    然后将这个要过滤的字符串添加到pattern[]数组的末尾。至此,整个命令的解析就已经完成了。接下来就要开始执行过滤操作了。
        接下来就是
    这个grepbehave在grep_main()的首部就已被赋值。对于我们由串口键入命令的情况来说,其值恒为GREP_BASIC。这里,不必理会。
     
        接下来是
    这一块的作用我也不知道。程序一般会走694行。看695行的函数名fastcomp(),猜它应该是一个快速对比的功能。这个函数的定义位于
    1 ./system/core/toolbox/grep/fastgrep.c
    有兴趣的话可以去详细阅读一下源码。这个函数返回0表示成功,返回-1表示失败。经测试,都是返回-1。此处略过这块代码。
     
        接下来是搜索前的最后的设置。
    707行的lbflag是行缓冲。目前并不知道它的具体作用。经测试,由串口键入命令的方式,该值一般为0。此处暂时不予理会。
    710行的Hflag,。该值默认为0。
    由713行,当键入的过滤字符串只有一个时,就进行过滤操作,然后退出grep程序。
    否则,就执行下面的代码。多个过滤字符串中后面的将会被认为是文件或目录的路径。这样就会被系统理解为在这个目录或文件中执行搜索命令。
    dirbehave在键入的参数中有'-r'时其值会为DIR_RECURSE。其它情况,则执行else分支。
    这里,暂时不讨论指定文件或目录的情况。
     
    procfile()函数位于
    1 ./system/core/toolbox/grep/util.c
        这个函数内部定义了几个结构体变量。其中file与str结构体的定义位于
    1 ./system/core/toolbox/grep/grep.h

        file结构体结构简单,内部仅保存文件的句柄及这个文件是否是二进制文件的标志。str结构体也简单。其中*dat保存的是当前剩余要过滤的数据。对于在串口中使用grep而言,就是当前获得的文件流中的所有内容。
    对于只有一个过滤字符串的情况,会执行上图所示代码中的197行代码块。且对于在串口中键入命令的情况,198行的fn得到的值是(standard input)。即标准输入流,而非某个文件流。接着去grep_open(NULL)函数,这个函数位于
    1 ./system/core/toolbox/grep/file.c
    这个函数的结构也简单,
    这个函数的重点在于第241行处。它会将文件结构体的文件句柄指向标准输入流处。
        接着再回到util.c处继续往下执行,现在就到了从流中过滤数据的时候了。核心代码如下图所示,
    上面这个for循环语句就是用来过滤关键字并将结果打印在控制台用的。在第226行for循环的条件判断处可以看到两个状态位lflag与qflag。前面的状态位是标志是否只打印文件名,后面的状态位则是标志是否静默搜索(将结果输出到文件或其它什么的)。
     
        上图所示代码中,第228行的if语句的作用就是逐行读取源数据ln.dat。然后跳到第242行。这里调用了procline函数。上面的procfile是处理整个文件流,读取出一行数据后就传到procline中作进一步处理。随后procline在匹配到符合条件的行后,传到printline函数中打印出来。这三个函数均在util.c文件中。具体的实现流程就不再赘述了。
     
    grep的详细流程在我看来是很复杂的,没有必要去弄懂这个流程中每一行代码的意义,只需要了解个大概流程就好了,毕竟是抱着扩充知识面而非研究代码的目的去学习的。
    +++
  • 相关阅读:
    rsync免密码远程复制文件
    维护中常用的k8s和docker命令
    小程序插件集成functional-page-navigator真机调试报错
    docker私有仓库操作(搭建、运行、添加、删除)
    国内不fq安装K8S四: 安装过程中遇到的问题和解决方法
    国内不fq安装K8S三: 使用helm安装kubernet-dashboard
    国内不fq安装K8S二: 安装kubernet
    国内不fq安装K8S一: 安装docker
    机器学习笔记8:XGBoost
    机器学习笔记7:矩阵分解Recommender.Matrix.Factorization
  • 原文地址:https://www.cnblogs.com/chorm590/p/6727891.html
Copyright © 2020-2023  润新知