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

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

bash alias及function展开  

2013-05-22 23:17:24|  分类: bash分析 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、alias展开
1、展开的时机
别名展开应该是bash中展开最早的一个内置展开,它的展开早于变量的展开、也早于函数的查找,它甚至超越了通常的语法分析,是在词法分析的阶段直接替换,从这个意义上讲,alias相当于C语言中的宏。
看一下alias的展开在
static int
read_token_word (character)
  /* Aliases are expanded iff EXPAND_ALIASES is non-zero, and quoting
     inhibits alias expansion. */
  if (expand_aliases && quoted == 0)
    {
      result = alias_expand_token (token);
      if (result == RE_READ_TOKEN)
    return (RE_READ_TOKEN);
      else if (result == NO_EXPANSION)
    parser_state &= ~PST_ALEXPNEXT;
    }
可以看到,这个别名的展开在词法分析阶段已经完成,所以相当于C语言中的宏,原始内容对上层的语法分析不可见。
但是这里明显有一个expand_alias来控制是否展开的开关。
2、何时使能该标志
static void
init_interactive ()
{
  expand_aliases = interactive_shell = startup_state = 1;
  interactive = 1;
}

static void
init_noninteractive ()
{
#if defined (HISTORY)
  bash_history_reinit (0);
#endif /* HISTORY */
  interactive_shell = startup_state = interactive = 0;
  expand_aliases = posixly_correct;    /* XXX - was 0 not posixly_correct */
  no_line_editing = 1;
#if defined (JOB_CONTROL)
  set_job_control (0);
#endif /* JOB_CONTROL */
}
如果是交互式shell,那么别名展开是使能的,但是如果是执行一个脚本,则别名展开默认是禁止的,因为posixly_correct默认是关闭的。
3、测试一下结论
[root@Harry scriptalias]# cat script.sh
#!/bin/sh
alias myls="ls -l"
myls
[root@Harry scriptalias]# bash script.sh
script.sh: line 3: myls: command not found
[root@Harry scriptalias]# sh script.sh
total 4
-rw-r--r--. 1 root root 34 2013-05-22 22:11 script.sh
[root@Harry scriptalias]# source script.sh
total 4
-rw-r--r--. 1 root root 34 2013-05-22 22:11 script.sh
[root@Harry scriptalias]# ll `which sh`
lrwxrwxrwx. 1 root root 4 2011-03-12 16:58 /bin/sh -> bash
[root@Harry scriptalias]#
使用bash解析并执行脚本时,执行错误,没有进行别名展开,其他的使用sh和bash都可以完成别名展开,尤其比较奇怪的是同一个脚本,使用sh和bash执行时输出不同,而sh是指向bash的软连接。
4、bash的特殊处理
set_shell_name (argv0)
  if (shell_name[0] == 's' && shell_name[1] == 'h' && shell_name[2] == '\0')
    act_like_sh++;
  if (shell_name[0] == 's' && shell_name[1] == 'u' && shell_name[2] == '\0')
    su_shell++;
如果程序的名称为sh,则设置act_like_sh变量,然后在main函数中
  /* If we are invoked as `sh', turn on Posix mode. */
  if (act_like_sh)
    {
      bind_variable ("POSIXLY_CORRECT", "y", 0);
      sv_strict_posix ("POSIXLY_CORRECT");
    }
也就是开启了posix选项,这种通过可执行文件的名字来自我调整的方式是不是很坑爹呢?
二、函数相关
1、函数定义的解析
bash-4.1\parse.y
function_def:    WORD '(' ')' newline_list function_body
            { $$ = make_function_def ($1, $5, function_dstart, function_bstart); }

    |    FUNCTION WORD '(' ')' newline_list function_body
            { $$ = make_function_def ($2, $6, function_dstart, function_bstart); }

    |    FUNCTION WORD newline_list function_body
            { $$ = make_function_def ($2, $4, function_dstart, function_bstart); }
    ;
make_function_def (name, command, lineno, lstart)
  return (make_command (cm_function_def, (SIMPLE_COM *)temp));
2、函数定义语句的执行
这个要和函数的引用区分,这里的定义只是对于函数定义本身这个语法单位的处理
int
execute_command_internal (command, asynchronous, pipe_in, pipe_out,
              fds_to_close)
    case cm_function_def:
      exec_result = execute_intern_function (command->value.Function_def->name,
                         command->value.Function_def->command);
      break;
static int
execute_intern_function (name, function)
     WORD_DESC *name;
     COMMAND *function;
……
  var = find_function (name->word);
  if (var && (readonly_p (var) || noassign_p (var)))
    {
      if (readonly_p (var))
    internal_error (_("%s: readonly function"), var->name);
      return (EXECUTION_FAILURE);
    }

  bind_function (name->word, function);
对于函数定义的执行只是简单的把函数定义和函数名称进行绑定。
3、函数引用执行及查找
函数的引用和执行是作为简单命令来执行,只是会优先搜索一下函数定义
execute_simple_command (simple_command, pipe_in, pipe_out, async, fds_to_close)
……
  builtin = (sh_builtin_func_t *)NULL;
  func = (SHELL_VAR *)NULL;
  if ((simple_command->flags & CMD_NO_FUNCTIONS) == 0)
    {
      /* Posix.2 says special builtins are found before functions.  We
     don't set builtin_is_special anywhere other than here, because
     this path is followed only when the `command' builtin is *not*
     being used, and we don't want to exit the shell if a special
     builtin executed with `command builtin' fails.  `command' is not
     a special builtin. */
      if (posixly_correct)
    {
      builtin = find_special_builtin (words->word->word);
      if (builtin)
        builtin_is_special = 1;
    }
      if (builtin == 0)
    func = find_function (words->word->word);
    }
……
  /* This command could be a shell builtin or a user-defined function.
     We have already found special builtins by this time, so we do not
     set builtin_is_special.  If this is a function or builtin, and we
     have pipes, then fork a subshell in here.  Otherwise, just execute
     the command directly. */
  if (func == 0 && builtin == 0)
    builtin = find_shell_builtin (this_command_name);
先后进行了函数定义和内置命令的搜索,但是根据posix标准,一些特殊的shell内置函数的搜索要早于函数的定义,这些特殊的内置函数为
/* The Posix.2 so-called `special' builtins. */
char *special_builtins[] =
{
  ":", ".", "source", "break", "continue", "eval", "exec", "exit",
  "export", "readonly", "return", "set", "shift", "times", "trap", "unset",
  (char *)NULL
};
但是应该不会有人把这些定义为函数吧。
三、bash解析和执行的单位
在bash执行的时候,它的语法解析和执行粒度是如何呢?是以结构为单位解析并执行还是整个文件语法分析结束之后再执行呢?
看一下bash的语法文件
inputunit:    simple_list simple_list_terminator
            {
              /* Case of regular command.  Discard the error
                 safety net,and return the command just parsed. */
              global_command = $1;
              eof_encountered = 0;
              /* discard_parser_constructs (0); */
              if (parser_state & PST_CMDSUBST)
                parser_state |= PST_EOFTOKEN;
              YYACCEPT;
            }
    |    '\n'
            {
              /* Case of regular command, but not a very
                 interesting one.  Return a NULL command. */
              global_command = (COMMAND *)NULL;
              if (parser_state & PST_CMDSUBST)
                parser_state |= PST_EOFTOKEN;
              YYACCEPT;
            }
    |    error '\n'
再细看一下simple_list的定义就可以知道,只要一个bash定义的语法结构完成解析,该语法解析就会退出,退出之后执行
reader_loop(该函数对yyparse的调用为read_command--->>parse_command--->>yyparse)函数的
execute_command (current_command);
函数,所以说bash并没有把整个文件完全读入、解析、检测完语法错误之后再从头执行,而是简单的以bash语法单位为粒度进行读取和执行,所以之后即使有语法错误,之前的指令依然会被执行。
  评论这张
 
阅读(1068)| 评论(0)
推荐 转载

历史上的今天

评论

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

页脚

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