注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

bash命令行处理及内核对script处理  

2013-06-16 23:52:07|  分类: bash分析 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、内核对script脚本的处理
对于script脚本来说,虽然简单,但是也是内核中直接识别的一种格式,和晦涩难懂的ELF格式一样,不过内核中对于script文件的解析代码只有100行左右。虽然简单,但是毕竟是内核中的直接内置支持格式,就好象弼马温虽然是个无品的官,但是毕竟是天上的神仙。
内核中对于script格式的可执行文件的解析和加载位于binfmt_script.c文件中,内核中对于脚本文件的解析和执行整个核心代码就比较少,有多少呢,少到我可以直接把它拷贝过来而不显得占用地方:
static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)
{
    const char *i_arg, *i_name;
    char *cp;
    struct file *file;
    char interp[BINPRM_BUF_SIZE];
    int retval;

    if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!') ||
        (bprm->recursion_depth > BINPRM_MAX_RECURSION)) 在2.6.12内核中这个地方不是这样,当时的表示形式是(bprm->sh_bang),也就是说脚本文件不能作解析器。现在这个地方有一个更加宽泛的限制,就是可以递归四次,看来内核对用户态的代码还是处处设防的,#define BINPRM_MAX_RECURSION 4
        return -ENOEXEC;
    /*
     * This section does the #! interpretation.
     * Sorta complicated, but hopefully it will work.  -TYT
     */

    bprm->recursion_depth++;
    allow_write_access(bprm->file);
    fput(bprm->file);
    bprm->file = NULL;

    bprm->buf[BINPRM_BUF_SIZE - 1] = '\0';
    if ((cp = strchr(bprm->buf, '\n')) == NULL)
        cp = bprm->buf+BINPRM_BUF_SIZE-1;
    *cp = '\0';
    while (cp > bprm->buf) {从后到前扫描脚本文件第一行,将搜索的空白(空格和制表符)删除
        cp--;
        if ((*cp == ' ') || (*cp == '\t'))
            *cp = '\0';
        else
            break;
    }
    for (cp = bprm->buf+2; (*cp == ' ') || (*cp == '\t'); cp++);+2表示跳过#!两个字符,同样是删除空白
    if (*cp == '\0')
        return -ENOEXEC; /* No interpreter name found */
    i_name = cp;接下来的作为解释器名字
    i_arg = NULL;
    for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
        /* nothing */ ;
    while ((*cp == ' ') || (*cp == '\t'))
        *cp++ = '\0';
    if (*cp)
        i_arg = cp;剩余的部分全部作为参数,传递给解释器
    strcpy (interp, i_name);
    /*
     * OK, we've parsed out the interpreter name and
     * (optional) argument.
     * Splice in (1) the interpreter's name for argv[0]
     *           (2) (optional) argument to interpreter
     *           (3) filename of shell script (replace argv[0]) 有时候,当你看不懂代码的时候,那就看看注释,内核代劳做的功能
     *
     * This is done in reverse order, because of how the
     * user environment and arguments are stored.
     */
    retval = remove_arg_zero(bprm);
    if (retval)
        return retval;
    retval = copy_strings_kernel(1, &bprm->interp, bprm);
    if (retval < 0) return retval;
    bprm->argc++;
    if (i_arg) {
        retval = copy_strings_kernel(1, &i_arg, bprm);
        if (retval < 0) return retval;
        bprm->argc++;
    }
    retval = copy_strings_kernel(1, &i_name, bprm);
    if (retval) return retval;
    bprm->argc++;
    bprm->interp = interp;

    /*
     * OK, now restart the process with the interpreter's dentry.
     */
    file = open_exec(interp);
    if (IS_ERR(file))
        return PTR_ERR(file);

    bprm->file = file;
    retval = prepare_binprm(bprm);
    if (retval < 0)
        return retval;
    return search_binary_handler(bprm,regs);
}
二、bash对于命令行的解析
shell.c文件中main (argc, argv)函数
  /* Find full word arguments first. */
  arg_index = parse_long_options (argv, arg_index, argc);
……
  arg_index = parse_shell_options (argv, arg_index, argc);
……
  /* Bind remaining args to $1 ... $n */
  arg_index = bind_args (argv, arg_index, argc, 1);
大致的流程就是对于提供的所有参数,首先解析长选项命令,然后解析段选项(以单线开始的选项),然后剩余的参数绑定到从1到9的参数上。这一点是比较有意思的,因为内核做了转换之后,bash看到的argv[0]只是/bin/sh文件,但是在真正执行的脚本中,我们需要通过$0看到的是脚本文件本身,所以此时shell义不容辞的做了一个简单的转换,手动的将变量逐个绑定到位置变量中。
1、parse_long_options函数
static int
parse_long_options (argv, arg_start, arg_end)
     char **argv;
     int arg_start, arg_end;
{
  int arg_index, longarg, i;
  char *arg_string;

  arg_index = arg_start;
  while ((arg_index != arg_end) && (arg_string = argv[arg_index]) &&
     (*arg_string == '-'))
    {
      longarg = 0;

      /* Make --login equivalent to -login. */
      if (arg_string[1] == '-' && arg_string[2]) 如果是--形式,则跳过两个字符,当然如果是只有一个横杠,也没关系,只要后面能够搜索到就行
    {
      longarg = 1;
      arg_string++;
    }

      for (i = 0; long_args[i].name; i++)
    {
      if (STREQ (arg_string + 1, long_args[i].name))
        {
          if (long_args[i].type == Int)
        *long_args[i].int_value = 1;
          else if (argv[++arg_index] == 0)
        {
          report_error (_("%s: option requires an argument"), long_args[i].name);
          exit (EX_BADUSAGE);
        }
          else
        *long_args[i].char_value = argv[arg_index];

          break;
        }
    }
      if (long_args[i].name == 0)
    {
      if (longarg)
        {
          report_error (_("%s: invalid option"), argv[arg_index]);
          show_shell_usage (stderr, 0);
          exit (EX_BADUSAGE);
        }
      break;        /* No such argument.  Maybe flag arg. */如果遇到一个不能识别的长选项类型,则直接退出整个解析,也就是说长参解析是一个聚集在一起的,并且必须在命令行的最开始,遇到第一个非长参形式的变量之后退出长参解析,之后的不会被认为长参形式,这里也说明对于长参,使用单横杠bash也是可以识别的,所以所谓的长参和短参只是说参数是由一个还是多个字符组成的而已
    }

      arg_index++;
    }

  return (arg_index);
}
2、parse_shell_options函数
函数太长,只摘录一部分代码
/* Welcome to the world of Un*x, where everything is slightly backwards. */
#define FLAG_ON '-'
#define FLAG_OFF '+'

static int
parse_shell_options (argv, arg_start, arg_end)
     char **argv;
     int arg_start, arg_end;
{
  int arg_index;
  int arg_character, on_or_off, next_arg, i;
  char *o_option, *arg_string;

  arg_index = arg_start;
  while (arg_index != arg_end && (arg_string = argv[arg_index]) &&
     (*arg_string == '-' || *arg_string == '+'))
    {
      /* There are flag arguments, so parse them. */
      next_arg = arg_index + 1;

      /* A single `-' signals the end of options.  From the 4.3 BSD sh.
     An option `--' means the same thing; this is the standard
     getopt(3) meaning.
*/
      if (arg_string[0] == '-' &&
       (arg_string[1] == '\0' ||
         (arg_string[1] == '-' && arg_string[2] == '\0')))
    return (next_arg);
}
这个代码注释里有不少有意思的说明,
第一个就是bash对于选项的打开和关闭使用的是一个和通常理解相反的形式,打开的时候使用的是减号,关闭的时候是使用加号,所以这里的作者在这里也表示了对于这种方法的无奈,where everything is slightly backwards。
第二个就是通过两个双减号不带任何内容表示所有参数的结束,结合这个可以看一下parse_long_options 函数中对于这个地方还是有预防的。而且之后我们还会说道这个特性在什么情况下是有用的。作者说这个是getopt函数的通用特征,所以大部分的Linux工具如果使用getopt库函数来解析命令行选项,那么这个特性都是可以保持的。
在man 3 getopt函数中的说明
       By default, getopt() permutes the contents of argv as it scans, so that
       eventually all the non-options are at the end.   Two  other  modes  are
       also  implemented.   If  the first character of optstring is '+' or the
       environment variable POSIXLY_CORRECT is  set,  then  option  processing
       stops  as  soon  as a non-option argument is encountered.  If the first
       character of optstring is '-', then  each  non-option  argv-element  is
       handled  as if it were the argument of an option with character code 1.
       (This is used by programs that were written to expect options and other
       argv-elements  in  any  order  and  that care about the ordering of the
       two.)  The special argument  "--"  forces  an  end  of  option-scanning
       regardless of the scanning mode
.
三、对于shell脚本的影响
1、在脚本中传递参数
[root@Harry scriptopt]# cat opt.sh
#!/bin/sh -x
echo hello world

[root@Harry scriptopt]# ./opt.sh
+ echo hello world
hello world
2、在脚本命令行传递参数
[root@Harry scriptopt]# cat opt.sh
#!/bin/sh
echo hello world

[root@Harry scriptopt]# ./opt.sh -x
hello world
[root@Harry scriptopt]#
直接把-x加到脚本后面无效,因为这个参数是在opt.sh文件之后,而shell对于选项的解析在遇到一个不识别的选项后结束,也就是在opt.sh后就终止了,后面的-x留给脚本使用,受用!
四、删除以减号开始的文件
按照标准流程,我们可以在网络上搜索一下解决方法,这里有一个比较巧妙的解决方法删除文件。作者的意思比较巧妙,对于下面的文件删除问题
[root@Harry scriptopt]# ll
total 4
-rwxr-xr-x. 1 root root 29 2013-06-17 00:32 opt.sh
-rw-r--r--. 1 root root  0 2013-06-17 00:39 -weirdfile
[root@Harry scriptopt]# rm -weirdfile
rm: invalid option -- 'w'
Try `rm ./-weirdfile' to remove the file `-weirdfile'.
Try `rm --help' for more information.
1、第一种方法
作者通过的方式就是加一个当前路径,那就是通过
rm ./-weirdfile
来删除文件,这种方法感觉很巧妙,有四两拨千斤的感觉。
2、第二种方法利用opt方式
这种方法就是
Alternatively, you can use
rm -- --testing
touch -- --testing
3、第三种方法
利用ls和find命令
[root@Harry scriptopt]# ll -i
total 4
529044 -rwxr-xr-x. 1 root root 29 2013-06-17 00:32 opt.sh
529003 -rw-r--r--. 1 root root  0 2013-06-17 00:39 -weirdfile
[root@Harry scriptopt]# find -inum 529003 -exec rm {} \;
[root@Harry scriptopt]# ll
total 4
-rwxr-xr-x. 1 root root 29 2013-06-17 00:32 opt.sh
[root@Harry scriptopt]#
  评论这张
 
阅读(849)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017