学习bash第二版-附录三 可载入内置命令

  bash 2.0引入了一种增加shell灵活性的新特性:动态可载入内置命令。在动态可载入系统上,你可以用C编写自己的命令,将之编译到共享对象中,并在shell内使用enable内置命令(请参见第七章以了解enable选项的细节部分)在任意时刻载入它们。
  本附录讨论如何编写一个内置命令,以及如何在bash中载入它。这里假定你在编写、编辑和链接C程序方面经验丰富。
  bash包在目录examples/loadables/下包含许多预编写的内置命令。可以通过把文件Makefile中和系统相关部分的注释去掉并键入make来构建它们。我们取用其中之一tty,使用它来做一半内置命令的“案例分析”。
  tty模拟标准UNIX命令tty。它打印出与标准输入相连的终端名字,该命令也像其他命令一样,如果设备是一个TTY,则返回真;如果不是,则返回假。另外,它带有一个选项-s,指定输出为空,即打印为空,只返回结果。
  该命令的C代码可分成3个不同部分:实现命令功能的代码、帮助文本信息定义和描述命令使其可被bash访问的结构。
  描述结构很直接,采取下列形式:
  
  struct builtin structname = {
      "builtin_name",
      function_name,
      BUILTIN_ENABLED,
      help_array,
      "usage",
      0
  };
  
  builtin_name是其在shell中出现时的名字。下一域function_name是实现该命令的C函数名。下面马上会介绍它。BUILTIN_ENABLED是命令的初始状态,或是被使能,或是被屏蔽。应总将该域设置为BUILTIN_ENABLED。help_array是当对命令使用help时显示的字符串的数组。usage是帮助的缩写形式;接着是命令和其选项,最后一个域应被设置为0.
  例子中,称该命令为tty,C函数名为tty_builtin,帮助数组为tty_doc。用法字符串为tty [-s]。最后的结构如下:
  
  struct builtin tty_struct = {
      "tty",
      tty_builtin,
      BUILTIN_ENABLED,
      tty_doc,
      "tty [-s]",
      0
  };
  
  接下来的部分是完成该项工作的代码,如下:
  
  tty_builtin (list)
      WORD_LIST *list;
  {
      int opt, sflag;
      char *t;
   
      reset_internal_getopt ();
      sflag = 0;
      while ((opt = internal_getopt (list, "s")) != -1)
      {
        switch (opt)
        {
            case 's':
                sflag = 1;
                break;
            default:
                builtin_usage ();
                return (EX_USAGE);
        }
      }
      list = loptend;
   
      t = ttyname (0);
      if (sflag == 0)
          puts (t ? t : "not a tty");
      return (t ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
  }
  
  通常会传递给命令函数一个到类型WORD_LIST列表的指针。如果该命令不带任何选项,则必须在进一步处理前调用no_options(list)并检查其返回值。如果返回值为非0,则函数应立即返回,并带有返回值EX_USAGE。
  必须使用internal_getopt而不是使用标准C库getopt来处理命令选项。另外,也必须首先通过调用reset_internal_getopt重置选项处理。
  选项处理可以以标准形式执行,但如果选项不正确,就应该返回EX_USAGE。选项处理后留下的参数被指向loptend。一旦函数完成,它应返回值EXECUTION_SUCCESS或EXECUTION_FAILURE。
  在这里的tty命令例子中,我们要调用标准C库进程ttyname,如果未给出-s选项,则打印出tty的名字(如果设备不是tty,则打印“not a tty”)。然后函数返回成功或失败,取决于ttyname调用的结果。
  最后一部分是帮助函数,它是一个简单的字符串数组,数组的最后一个元素为NULL。当对该命令运行help时,每个字符串都被打印到标准输出中。因此,应该保持字符串长度小于等于76(一个长度80的标准显示减去4个字符的页边距)。在tty中,帮助文本如下:
  char *tty_doc[] = {
    "tty writes the name of the terminal that is opened for standard",
    "input to standard output.  If the `-s' option is supplied, nothing",
    "is written; the exit status determines whether or not the standard",
    "input is connected to a tty.",
    (char *)NULL
  };
  
  向代码中加入的最后内容必须是C头文件。这些为stdio.h,以及bash头文件config.h, builtins.h, shell.h和bashgetopt.h。
  完整的C程序如下:
  #include "config.h"
  #include <stdio.h>
  #include "builtins.h"
  #include "shell.h"
  #include "bashgetopt.h"
   
   
  extern char *ttyname ();
   
  tty_builtin (list)
      WORD_LIST *list;
  {
      int opt, sflag;
      char *t;
   
      reset_internal_getopt ();
      sflag = 0;
      while ((opt = internal_getopt (list, "s")) != -1)
      {
          switch (opt)
          {
              case 's':
                  sflag = 1;
                  break;
              default:
                  builtin_usage ();
                  return (EX_USAGE);
          }
      }
      list = loptend;
   
      t = ttyname (0);
      if (sflag == 0)
          puts (t ? t : "not a tty");
      return (t ? EXECUTION_SUCCESS : EXECUTION_FAILURE);
  }
   
  char *tty_doc[] = {
      "tty writes the name of the terminal that is opened for standard",
      "input to standard output.  If the `-s' option is supplied, nothing",
      "is written; the exit status determines whether or not the standard",
      "input is connected to a tty.",
      (char *)NULL
  };
   
  struct builtin tty_struct = {
      "tty",
      tty_builtin,
      BUILTIN_ENABLED,
      tty_doc,
      "tty [-s]",
      0
  };
  
  下面需要进行编译并将其链接为一个动态共享对象。不过,不同的系统有不同的编译动态共享对象的方式。表C-1列出了一些常用系统以及编译和链接tty.c所需的命令。将archive替换为bash存档文件的最上层路径即可。
  
  表C-1    共享对象编译
  系统                命令
  SunOS 4             cc -pic -Iarchive -Iarchive/builtins -Iarchive/lib -c tty.c
                      ld -assert pure-text -o tty tty.o
  SunOS 5             cc -K pic -Iarchive -Iarchive/builtins -Iarchive/lib -c tty.c
                      cc -dy -z text -G -i -h tty -o tty tty.o
  SVR4, SVR4.2, Irix  cc -K PIC -Iarchive -Iarchive/builtins -Iarchive/lib -c tty.c
                      ld -dy -z text -G -h tty -o tty tty.o
  AIX                 cc -K -Iarchive -Iarchive/builtins -Iarchive/lib -c tty.c
                      ld -bdynamic -bnoentry -bexpall -G -o tty tty.o
  Linux               cc -fPIC -Iarchive -Iarchive/builtins -Iarchive/lib -c tty.c
                      ld -shared -o tty tty.o
  NetBSD, FreeBSD     cc -fpic -Iarchive -Iarchive/builtins -Iarchive/lib -c tty.c
                      ld -x -Bshareable -o tty tty.o
  
  进一步的例子在存档文件中的文件实例examples/loadables/Makefile中给出。
  编译和链接完程序后,应该有一个称为tty的共享对象。要将之载入bash,可以键入enable -f path/tty tty。这里path为共享对象的完整路径。你可以使用-d选项在任意时刻删除一个载入的命令,例如enable -d tty。
  可以把许多命令放在一个共享对象中。所需做的就是上面讲过的同样的C文件中命令的三个主要部分。然而,最好使每个共享对象命令的数量尽可能少。你可能会发现把类似的命令或协同工作的命令放在同一共享对象中是最好的。
  bash作为一个整体载入一个共享对象,因此如果你要从一个有20个命令的共享对象中载入1个命令,它可能同时载入所有的20个命令(但只有1个可用)。正因为如此,要保持命令数量尽量少以节省载入内存,不载入不必要的内容,并将类似命令分组以便用户启用其中之一时,所有的类似命令均被载入,并在可用命令的内存中做好了准备。
 

猜你喜欢

转载自blog.csdn.net/chenzhengfeng/article/details/81663963
今日推荐