【原创】Bash源码分析(一)

作者简介:15年通讯底层研发经验,熟悉linux/vxworks等实时操作系统的内核原理和实现,在虚拟化的openstack,kubernetes,docker等领域也初有涉猎。

摘要:本文讲述当下留下的linux的bash的源代码,通过代码分析和单步调试解析bash的运行流程,适合喜欢研究linux原理的高级用户。分析的源代码来自gnu的开源项目https://git.savannah.gnu.org/git/bash.git,例子是作者自己编写,可以随意引用。

1 引言

Bash这个程序作为一个linux的用户,用的实在太频繁了,但一般局限于会用就结束了,一直没机会研究bash本身的原理。因工作需要,调试一个bash的cpu冲高问题,趁此机会对bash的源码做了一些研究,希望能对大家有点帮助。

2 linux的各种主流shell介绍

现在一般使用的shell有sh,bash和csh这几种,我们这里主要说的是bash,其他shell的源代码逻辑也差不多。

3 bash使用到的主要数据结构介绍

3.1 COMMAND

COMMAND是所有数据结构的纲,从这里可以看出一个bash实际能执行的语句有14种,分别位for,case,while,fi,connection,simple_com,function,group,select,arith,cond,arith_for,subshell,coproc,其中select,arith,cond,arith_for这4个命令需要打开对应的编译开关之后才能执行。 除了下面的这个union外,另外几个属性分别对应命令类型,行号和执行环境控制参数。其中控制参数有很多,每个控制参数占用一个bit位,包括是否启动子shell,是否忽略exit值等。


这些flag可以在bash启动shell脚本时设置,或者在shell脚本内部调用set指令来设置,一般用户不怎么关注,高阶用户可以看看:

3.2 FOR_COM


FOR_COM对应的shell语句是for name in map_list; do action; done 从结构体定义可以看出,除了和COMMAND相同的flags和行号外,for语句是有一个变量名,一个列表和一个递归的COMMAND组成的,实际for循环执行过程中也是将列表中的每个元素拿出来赋值给变量名,并执行action中的脚本段。 从这里的flags,可以看出,每条命令的flags是可以单独设置的,本条命令设置的控制参数可以不影响其他命令的控制参数。

3.3 CASE_COM


对照下面的脚本,可以看出,先判断一个变量,变量判断晚走到复合语句clauses,注意clause最终实现的时候是一个单向链表,链表中每个元素由一个样式的列表和一个执行体action来组成。


3.4 WHILE_COM


WHILE_COM比较简单,主体有两部分组成,判断条件和执行体。上篇文章调试过程中导致CPU冲高到100%的例子就是用的WHILE_COM,不过例子中的WHILE_COM的判断条件留空,相当于永远为true。


3.5 IF_COM


IF_COM由3段组成,条件判断,条件为true时的执行体和条件为false时的执行体,其中false情况下的执行体可以为空。


从IF_COM的定义看,3部分都可以是复杂的COMMAND结构,所以嵌套起来也可以做的非常复杂,例如可以在test部分通过执行脚本,依靠脚本的返回值来判断是应该执行true_case还是false_case。

3.6 CONNECTION


CONNECTION由4个属性组成:ignore字段对应其他命令结构种的flags,但对连接命令实际上没有用first对应第一条命令,second对应第二条命令,connector对应两条命令之间的连接符。难道只能两个命令一起用,不能多余两个命令一起调用?显然不是,一个CONNECTION对应一个连接符连接起来的两段命令,每段命令又可以是一个CONNECTION,这样就形成了级联的效果。 CONNECTION有3种:AND_AND对应“&&”,表示first执行返回结果为0的时候执行second;OR_OR对应“||”,表示first执行返回结果为非0的时候执行second;分号对应的connector还是分号,表示无论first执行结果是0还是非0,都执行second。是不是有点像C语言里面的&&,||和;?


上面的三个例子别真的执行,后果很严重(:))。第一条表示删除$dir对应值的目录中的所有文件;第二条表示$dir不存在的时候删除当前目录下面的所有文件;第三条表示,如果$dir存在就删除$dir目录下的所有文件,如果不存在就删除当前目录下面的所有文件。

3.7 SIMPLE_COM



SIMPLE_COM按字面意思就是简单命令,结构体由四部分组成,通用的flags,行号line,命令队列WORD_LIST,重定向队列REDIRECT。 WORD_LIST队列比较容易理解。一堆命令的集合:


其中单个命令还可以独立设置flags:


REDIRECT就是重定向的意思,这里也有很多种重定向,结构体的各个属性的含义:next,组成重定向链表的指针;redirector,重定向的源;rflags,重定向时使用的私有flags;flags,打开重定向目标文件时的flags;instruction,重定向的实际功能指令,这个又有很多种,下面会详细描述;redirectee,重定向的目的文件描述符或者文件名;here_doc_eof,本地文件。


从REDIRECTEE的定义看,它既可以是一个文件描述符,例如0表示标准输入,1表示标准输出,2表示错误输出,也可以是一个文件名。



Bash支持二十种不同的重定向,后面会根据bash的源代码来一一解释一下具体内容(bash源代码的注释对重定向的含义理解也有很多帮助):


先来五种输出的重定向:普通输出,强制输出,错误和标准输出,叠加输出,错误和标准叠加输出。统一说一下几个概念,标准输出就是2级stdout,错误输出就是3级sdterr,强制的意思是文件存在的情况下会被先清空,再增加,叠加输出的意思是原有内容后面再增加。


再来九种输入和输出重定向:普通输入重定向,后台执行,输入和输出同时重定向,去掉空格的输入重定向,输入重定向,字符串作为输入,关闭重定向源(怎么还有这种应用场景?),复制输入,复制输出。 紧接着六种输入输出的重定向分别为输入剪切,输出剪切,字符指向的输入剪切和字符指向的输出剪切,字符指向的输入复制和字符指向的输出复制。

Bash的重定向真是博大精深!!

3.8 FUNCTION_DEF


FUNCTION_DEF由五部分组成,通用flags,起始行号,函数名,解析之后的函数执行体,如果函数定义在文件中,最后会有文件名。函数的定义也可以有入参,入参的提取和文件执行时类似的,都是走$1,$2类似的形式获得的。

3.9 GROUP_COM


GROUP_COM是个什么鬼?通过分析group的处理函数,发现group原来就是多个命令组成的命令段,一般用{}包围起来,从group_command_nesting变量的变化看,group是支持多层嵌套的。


3.10 SELECT_COM


SELECT_COM并不是每个版本的bash都存在,可以通过在bash里面敲help来确定其是否存在。下面这个bash 4.2.46的版本中是打开了select的开关的:


SELECT_COM的作用是为了生成一个简单的菜单,用户通过选择菜单来让系统执行对应的命令,常见的SELECT_COM是时区配置时使用的。


具体分析/usr/bin/tzselect源码时发现,为了做到各个shell之间的兼容,这个脚本写的比想象中要复杂的多。 首先要是一下版本号的记录:


跳过紧接着的注释,然后是版本兼容性判断,使用帮助:


然后很无聊的定义了一个完全不用的变量IFS,但用来定义IFS的newline后面倒是用过。 为了规避bug,还要把PS3清空。


终于进正题了,先选择大洲或者大洋:


根据大洲或者大洋,通过awk汇总对应的国家列表:


二级select,选择国家:


再次祭出awk,通过国家汇总时区列表:


第三重select,选择时区:


计算好时区之后,出现第四重select,确认是否要修改:


还要判断一下当前是cshell还是其他shell,指导用户将当前的时区改到shell的启动脚本里面去(老大你写了这么多代码,不能自动把这句加进去么?还是要手动加:))。


3.11 ARITH_COM


ARITH_COM也是需要开关打开的,不过当前默认用的bash都是支持的,这个命令的意思是算术表达式,算术表达式要用(())包起来,要不然bash会不知道你想当做算术表达式使用,如果执行“echo 1+1”会怎么样?bash认为它是一个文本,直接将文本本身显示出来了。


加上(())之后echo还是失败的:


再加一个$之后终于正常了:


实际操作的时候,发现[]包起来的算术表达式也能用,但不加$的时候不会报错,加了会触发求值:


通过查看代码,发现,只有(())是算术表达式:


而[]只是为了兼容posix.2d9的一种算术替换规则,也就是说$[1+1]是直接替换成了2,而(())还需要走到算术表达式求值过程(是不是有点饶)。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容

  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,380评论 0 5
  • #########################################################...
    半斋阅读 3,741评论 0 3
  • #########################################################...
    busy_living阅读 392评论 0 0
  • 最简单的例子 —— Hello World! 关于输入、输出和错误输出 BASH 中对变量的规定(与 C 语言的异...
    Kandy阅读 929评论 0 0
  • 欢迎关注微信公众号生信宝典:https://mp.weixin.qq.com/s/lWNp_6W_jLiogmtl...
    生信宝典阅读 400评论 0 1