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

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

bash如何实现for循环及“解释器”与“编译器”区别  

2012-01-27 18:14:31|  分类: bash分析 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、循环
循环是任何一种语言的重要基础,它和不同的数据结构(数组、链表、树、图等)结合,是实现代码复用性的基础。在C语言的for循环同样是大家最为常见的一个循环方式,使用起来非常顺手,以至于有些人一辈子就用过for这一种循环。看一下C语言的汇编代码,可以看到for循环的实现一定是需要外界帮助的,就是辅助添加一个跳转来完成循环。而bash作为一种脚本语言,它是不是也会为自己的for循环添加一个跳转来实现迭代呢?下面看一个gcc实现的循环体的反汇编:
[root@Harry fordisasm]# cat fordisasm.c
void loop(int cond)
{
    for(;cond;)
    cond++;
}
[root@Harry fordisasm]# gcc fordisasm.c -c
[root@Harry fordisasm]# objdump -d fordisasm.o

fordisasm.o:     file format elf32-i386


Disassembly of section .text:

00000000 <loop>:
   0:    55                       push   %ebp
   1:    89 e5                    mov    %esp,%ebp
   3:    eb 04                    jmp    9 <loop+0x9>
   5:    83 45 08 01              addl   $0x1,0x8(%ebp)
   9:    83 7d 08 00              cmpl   $0x0,0x8(%ebp)
   d:    75 f6                    jne    5 <loop+0x5> 这里可以看到,编译器添加了条件前向跳转指令来实现循环体(cond++)的迭代,bash是否/如何添加对应内容
   f:    5d                       pop    %ebp
  10:    c3                       ret   
[root@Harry fordisasm]#
二、bash循环体语法分析
parse.y
for_command:    FOR WORD newline_list DO compound_list DONE
……
|    FOR WORD newline_list IN word_list list_terminator newline_list DO compound_list DONE
            {
              $$ = make_for_command ($2, REVERSE_LIST ($5, WORD_LIST *), $9, word_lineno[word_top]);第二项为循环变量名,第五项word_list为变量取值集合,第九项为循环体
              if (word_top > 0) word_top--;
            }
调用的make_for_command的内容为,其中的变量命名体现了循环的一些要素,这里的条件和C语言的条件不同,不是一个表达式,而是一个集合,循环变量遍取指定集合中所有元素,并执行需要执行的动作。
COMMAND *
make_for_command (name, map_list, action, lineno)
     WORD_DESC *name;
     WORD_LIST *map_list;
     COMMAND *action;
     int lineno;
{
  return (make_for_or_select (cm_for, name, map_list, action, lineno));
}
三、循环执行
execute_for_command
{
……
  identifier = for_command->name->word; 取得迭代变量名。
  list = releaser = expand_words_no_vars (for_command->map_list);在循环体外将变量取值集合展开,参考上面的语法分析中说明。
……
  for (retval = EXECUTION_SUCCESS; list; list = list->next)这个for循环驱动对maplist的遍历。
    {
……
      v = bind_variable (identifier, list->word->word, 0);为变量绑定一个新的值,这个值取自maplist,
……
      retval = execute_command (for_command->action);执行循环体的内容。
    }
}
从这里可以看到,bash并没有添加任何跳转指令,那么它的迭代是如何实现的呢?这个就是一个思维转换的问题,也就是“解释器”(interpreter)和“编译器”(compiler)的区别,对于一个解释器来说,它要执行的脚本和解析器本身是一个“你中有我、我中有你”的人剑合一境界,它的执行就是直接在解释器内部执行;而对于编译器来说,它是一个一次性加工过程,当编译器读进输入、吐出输出,之后编译器和输入文件已经脱离干系,生成的可执行文件需要自己完成自己的迭代,所以它要有自己的跳转指令来实现迭代。对于一个解释器来说,它始终是和输入在一起的,所以,它直接使用自己内部的C语言循环来模拟输入脚本中的循环。这是两种不同的思路和实现方法,所以很多时候需要的是转换关键,而不是在以后的基础上打转。
  评论这张
 
阅读(805)| 评论(0)
推荐 转载

历史上的今天

评论

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

页脚

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