没有合适的资源?快使用搜索试试~ 我知道了~
首页开源软件架构揭秘:历史经验与设计启示
开源软件架构揭秘:历史经验与设计启示
需积分: 50 33 下载量 62 浏览量
更新于2024-07-17
1
收藏 9.04MB PDF 举报
"《开源软件架构》是由Greg Wilson 和 Amy Brown 联合撰写的书籍,该作品旨在填补软件行业中开发者缺乏系统学习历史和借鉴他人经验的空白。作者将视角聚焦于开源软件领域,邀请了48位经验丰富的开发者,他们分享各自软件的架构设计过程、决策背后的逻辑以及面临的挑战。这些开发者涵盖了不同规模和类型的项目,从简单的绘图工具到大型的可视化软件,每个案例都深入剖析了架构选择的原因,以及从中提炼出的设计原则和最佳实践。 本书特别强调了与建筑领域的相似性与区别,指出软件架构师需要像建筑师一样,研究和理解众多设计案例,而不是仅仅局限于自己的项目。然而,由于现实中开发者往往接触到的大型项目有限,他们容易重蹈覆辙,而这本书则提供了一个学习和提升软件架构能力的平台。 每位贡献者的背景丰富,例如Eric P. Allman,sendmail的创始人,以其在开源软件领域的深厚积累和广泛影响力,以及Keith Bostic,BerkeleyDB的架构师,展示了不同项目的技术深度和创新思考。通过阅读这些篇章,初学者能从中吸取先驱者的智慧,而无论经验丰富的开发者还是面临复杂设计问题的中级开发者,都能从中找到启发和参考。 书中不仅探讨了技术层面的问题,如可扩展性、客户端-服务器架构等,还包含了关于软件设计的艺术性和科学性的思考。《开源软件架构》是一部旨在打破行业壁垒,促进知识传承和进步的重要资源,对于软件工程教育和实践具有深远的影响。"
资源详情
资源推荐
![](https://csdnimg.cn/release/download_crawler_static/11227046/bg10.jpg)
多字节字符的处理功能是在shell出现之后很久才加入进来的,因此其设计原则是尽可能小地影响已有代码。如果本地设置
(locale)支持多字节字符,shell将输入存储在字节缓冲区中(简单的C语言字符),但是会将它们作为多字节字符进行处
理。readline知道如何显示多字节字符(关键问题是一个多字节字符占用多少屏幕空间,以及屏幕上显示一个多字节字符需要使
用多少个字节),如何在一行中移动一个字符(不是一个字节)的位置,等等。除了这些问题,多字节字符不会怎么影响输入处
理。但需要注意的是,shell的其它部分在处理输入时需要考虑到多字节字符的影响(后面会有介绍)。
3.4. 解析解析
解析引擎的第一步工作是词法分析:将字符流分割成单词(word),然后赋予其意义。单词(word)是解析器操作的基本单元,是
由元字符分隔的字符序列。简单的元字符如空格和制表符(tab),shell语言中的特殊字符也可构成元字符(如分号和&符号)。
Tom Duff曾在他的关于rc的文章(the Plan 9 shell)中说过,shell的一个历史遗留问题是没有人真正知道什么是Bash的语
法。Posix的shell委员会最终还是发布一个Unix Shell的标准语法,虽然这个语法仍然存在大量的上下文依赖关系,而且还不
能兼容以前的一些代码,但无疑这个标准是目前最好的,它的发布值得赞赏。
Bash解析器源于早期版本的Posix语法,而且,据我所知,它也是唯一一个使用Yacc或Bison实现的Bourne风格的shell。这带
来了一些麻烦,shell语法本身并不特别适合yacc风格的语法解析,需要采用一些复杂的语法分析,而且解析器和词法分析器
之间也要通力合作。
执行过程中,词法分析器从readline或其它输入来源中获取字符行,根据元字符将它们分割成记号(token),并根据上下文定位
这些记号,然后将其传给解析器组合成语句和命令。这个过程中涉及了大量的上下文,比如,获得的单词可能是保留字,可能
是标识符,可能是赋值语句的一部分,也可能是其它单词。下面是一个完全合法的命令:
for for in for; do for=for; done; echo $for
这个命令的作用是打印"for"这个字符串。
在这里顺便简单介绍一下别名(aliasing)。Bash支持使用任意文本(称为别名alias)替换简单命令的第一个单词(word),这个替换
过程完全是基于文本的。因而别名可以改变shell的语法,有的别名甚至实现了一个Bash不支持的复合命令。别名完全是在
Bash解析器的词法分析阶段实现的,而解析器必须告诉分析器(analyzer)什么时候允许展开别名。
和许多编程语言一样,shell支持字符的转义,用来改变字符的原有含义,使得一些元字符(如&)可以出现在命令中。Bash中三
种类型的引用,相互之间稍有不同,对包含的文本的解析方式也不尽相同。第一种是反斜划线(backslash),用来转义后面的
一个字符。第二种是单引号,它禁止对包含的文本进行解析。第三种是双引号,它阻止部分解析,但是允许一些单词(word)的
展开(处理反斜划线的方式也不相同)。词法分析器解译被引用的字符和字符串,防止它们被解析为保留字或元字符。但有两个
例外情况,$'...'和$"...",它们处理转义字符的方式和ANSI C一样,允许使用标准国际化函数来处理。前者使用更为广泛,后者
则因为案例太少而不为人知。
从解析器(parser)到分析器(analyzer)剩下的工作就非常直接了。解析器对一些状态进行编码,然后共享给分析器以支持依赖上
下文的语法分析。比如,词法分析器根据记号的类型将单词(word)分类为:保留字(依赖上下文),单词,赋值语句,等等。为
实现这个功能,解析器提供解析命令的进度信息,比如是否正在处理一个多行文本(有时候称为here-document),或者一
个case语句或条件命令,或者shell的扩展模式(extended shell pattern)或复合赋值语句。
在解析阶段,识别命令替换结点位置的大部分工作被封装在了一个函数中(parse_comsub),这个函数能够处理大量的shell语
法,并且包含了大量的冗余的读取记号的代码。而且,parse_comsub函数必须要知道here-document,shell命令,元字符和单
词(word)的边界,引用(quoting),以及什么时候允许保留字存在(比如,识别case语句中的保留字)。正确完成这些工作是比较
费时的。
在单词展开过程中展开一个命令替换,Bash使用解析器来获得这个结构正确的结束位置。这和eval命令将字符串变成命令的过
程有些类似,但不同的是命令的结束位置并不在字符串的结尾。为了实现这个功能,解析器必须将右括号视为一个合法的命令
终止符,这又导致了大量特殊情况的出现,并且要求词法分析器(在恰当的上下文中)将右括号标记为EOF。在递归触
发yyparse之前,解析器还必须能够保存和恢复解析器状态,因为在读取一条命令的中途,一个命令替换的解析和执行可能是展
开一个提示字符过程的一部分。因为输入函数实施了预读(输入来源可能是字符串,文件,或使用readline的终端),这个函数
必须最后能将输入指针(input pointer)恢复到正确的位置。这一点非常重要,不仅保证了输入不会丢失,而且使得命令替换的
展开函数能构造出正确的用于执行的字符串。
可编程的单词展开也存在类似的问题,因为在解析一个命令时允许执行其它任意命令,解决方案是在执行前保存状态,在执行
后恢复状态。
引用(quoting)是另外一个不兼容性和争论的来源。在Posix的shell标准发布二十年之后,标准工作组成员们仍然在争论引用的
那些难以理解的行为是否合理。但是Bash一直都只是一个实现上的参考,对制订标准是无能为力的。
解析器返回一个C结构体来表达一个命令(对于复合命令,这个结构体中可能还包含有其它命令),然后将其传递给shell的下一
个阶段:单词展开。命令结构体由一系列命令对象和单词列表组成。大部分的单词列表对应于各种变换,其意义随上下文的变
化而变化(下面会有解释)。
![](https://csdnimg.cn/release/download_crawler_static/11227046/bg11.jpg)
3.5. 单词展开单词展开
在解析阶段之后,在执行阶段之前,解析阶段产生的许多单词对应于一个或多个单词展开,比如$OSTYPE可以展开为"linux-
gnu"。
3.5.1. 参数和变量展开参数和变量展开
变量展开是用户最熟悉的。shell变量几乎都没有类型(少数例外),都被视为字符串。展开就是将这些字符串展开和转变为新的
单词和单词列表。
有的展开可以作用于变量的值上。编程人员可以获得变量值的子字符串,获得值的长度,删除匹配串首或串尾的子串,根据字
符串匹配替换子串,或者修改值中的大小写。
还有一些展开依赖于变量的状态:变量是否已经设置会导致不同的展开或赋值行为。比如,若parameter已经设置(并且不为
空),${parameter:-word}会被展开为parameter,否则展开为word。
3.5.2. 其他展开其他展开
Bash还支持其他多种展开,它们的规则都很诡异。处理过程中,最优先的展开是括号展开(brace exapansion),括号展开把
pre{one,two,three}post
转变为
preonepost pretwopost prethreepost
命令替换(command substitution)是shell执行命令与操纵变量的完美结合。shell运行一个命令,收集其输出,然后将输出作为
展开的值。
命令替换的一个问题在于命令的立即执行然后等待结果,此过程中shell无法传入输入。Bash使用一个称为进程替换(process
substitution)的功能来弥补这些不足,进程替换实际上是命令替换和管道的组合。和命令替换类似,Bash运行一个命令,但令
其运行于后台而不再等待其完成。关键在于Bash为这条命令打开了一个用于读和写的管道,并且绑定到一个文件名,最后展
开为结果。
下一个展开是波浪符展开(tilde expansion)。起初,~alan记号只是表示对Alan主目录的引用,多年后,这却渐渐变成了引用多
个不同目录的一种方式。
最后是算术展开(arithmetic expansion)。$((expression))会执行expression表达式,其规则与C语言表达式一致。expression的计算结
果变成展开的结果。
单引号和双引号之间最明显的差异在于变量展开(variable expansion)。单引号禁止所有展开,被包含的字符全部原封不动,
而双引号则允许部分展开。单词展开(word expansion),命令展开,算术展开,进程替换都可以在双引号中进行(双引号只处理
其结果),而括号展开和波浪符展开则被禁止。
3.5.3. 单词拆分单词拆分
单词展开的结果会被拆分(split),拆分的限定符来自于shell变量IFS的值。shell使用这种方式将单个单词拆分成多个。每
当$IFS
1
中的一个字符在结果中出现,Bash就会将单词一分为二。单引号和双引号中禁止单词拆分。
3.5.4. 名称替换名称替换(globbing)
结果拆分之后,shell将逐个处理之前展开得到的单词,使用一个模式来匹配已知的文件名,包括一些主要的文件目录。
3.5.5. 实现实现
如果shell的基本架构将流水线(pipeline)并行化,单词展开则是它自身的管道。单词展开的每个阶段处理一个单词,经过可能
的转换,然后将其传递给下一个展开阶段。等到所有的单词展开都已执行,命令就可以开始执行了。
Bash的单词展开的实现基于前面所说的基本数据结构。对解析器输出的单词逐个展开,由一个单词获得一个或多个单
词。WORD_DESC是一个通用的数据结构,足以封装单个单词展开所需的所有信息。其中,flags可用于编码单词展开阶段的信
息,并把信息传递给下一个阶段。例如,解析器使用一个标志位记录某个单词是一个赋值语句,并通知展开阶段和命令执行阶
段,而单词展开代码内部也使用标志位来禁止单词拆分或标记空字符串(比如"$x",其中$x未定义或为空值)。使用单个字符串来
表达展开后的单词,并且用某种字符编码来表达其它信息,实现起来并不容易。
对于解析器来说,单词展开的代码还需要处理多字节字符。比如,变量长度展开(${#variable})用于计算字符数目而非字节数,这
些代码能正确识别展开的结束位置或多字节字符存在情况下特殊字符的展开。
3.6. 命令执行命令执行
![](https://csdnimg.cn/release/download_crawler_static/11227046/bg12.jpg)
Bash内部流水线到命令执行阶段才真正开始执行命令。大部分时候,展开后的单词集合被分解为命令名称和参数集合,然后
作为文件传递给操作系统被读入和执行,而余下的单词则作为argv剩下的部分传递给操作系统。
到目前为止,描述的主要是简单命令(命令名加上一些参数)的执行,它们是最常用的类型,但是Bash还提供了更多的功能。
命令执行阶段的输入是一些解析器创建的命令结构,以及一些展开单词的集合。这是Bash编程语言真正开始起作用的时候。
编程语言使用变量和前面提到的展开来实现高级语言中常见的结构:循环(looping),条件(conditionals),替换(alternation),
分组(grouping),选择(selection),基于模式匹配的条件执行,表达式求值,以及其它一些shell特有的结构。
3.6.1. 重定向重定向
shell作为操作系统界面的表现之一是能够将输入和输出重定向到它所调用的命令上。重定向的语法反映了shell早期用户的复
杂操作:直到最近,用户仍然需要跟踪正在使用的文件描述符(file descriptor),并且显式地用数字来指定(除了标准输入,标准
输出和标准错误)。
重定向语法最近增加的一个功能是允许用户将shell定向到一个合适的文件描述符,然后赋值给一个指定的变量,因而用户不
需要再去选择文件描述符。这个功能减少了程序员跟踪文件描述符的负担,但是增加了额外的处理过程:shell必须将文件描
述符复制到正确的位置,还要确保它们赋值给指定的变量。这是又一个从词法分析器到解析器再到命令执行的信息传递过程的
例子:分析器找出包含变量赋值的重定向,解析器生成重定向对象并用一个标志位标记需要赋值,然后重定向代码根据这个标
志位确保文件描述符数字被赋值到正确的变量上。
实现重定向最难的地方是记得如何撤销重定向。shell有意不区分文件命令和内置命令,但是,无论是哪种命令,重定向的影
响范围不应该超过命令结束的时候
2
。因此,shell必须跟踪重定向,保证可以将其撤销,否则,内置命令的输出重定向会改变
shell的标准输出。Bash知道如何撤销每一种重定向,要么关闭之前分配的文件描述符,要么先保存文件描述符之后再使
用dup2恢复。这些过程使用的重定向对象和解析器创建的重定向对象是一样的,并且用同样的函数来处理。
因为多重重定向使用简单对象列表来实现,用来撤销的那些重定向也保存在一个单独的列表中。这个列表在命令完成时进行处
理,但是shell必须确认处理的时机,因为绑定到shell函数或"."内置命令上的重定向必须在它们执行完之前都一直有效。如果
没有命令被执行,内置命令exec将直接丢弃这个撤销列表,因为关联到exec上的重定向在整个shell环境中都是有效的。
另外一个复杂的地方来自Bash本身。Bourne shell的历史版本只允许用户操纵文件描述符0-9,而把10和10以上保留为内部使
用。后来,Bash放松了这个限制,允许用户在不超过进程文件打开数限制的条件时使用任意文件描述符。这意味着Bash必须
跟踪内部的文件描述符,包括那些不是直接由shell打开而是由外部库打开的那些文件描述符,这些内部文件描述符还可能时
不时被移动。因此,大量的文件描述符需要跟踪,有的使用启发式的close-on-exec标志位,有的重定向列表在整个命令执行
过程中都需要维护,然后在执行完成时处理或丢弃。
3.6.2. 内置命令内置命令
Bash中包含了大量的内置命令,这些命令由shell执行,但不会创建新进程。
使用内置命令最普遍的原因是维护和修改shell内部状态。比如cd命令就是一个Unix世界中的经典案例,cd命令需要修改当前工
作目录这个内部状态,因此不能用外部命令来实现。
Bash内置命令与shell其他部分使用相同的原语(primitive)。每个内置命令都是用C语言函数来实现的,以单词列表作为其参
数。这些单词来自于单词展开阶段,内置命令将其视为命令名和参数。大部分情况下,内置命令使用标准的展开规则,但也有
一些例外:Bash内置命令接受赋值语句作为其参数时(比如declare和export),赋值参数的展开规则与寻常变量赋值时的展开一
致。WORD_DESC结构体中的flags成员在这个地方也会发挥作用,在shell内部流水线(pipeline)不同阶段之间传递信息。
3.6.3. 简单命令执行简单命令执行
简单命令是shell中最常遇到的命令。其过程无非是:搜索和执行文件命令,收集退出状态,简单命令覆盖了shell当前大部分
功能。
shell的变量赋值(形如var=value的单词)本身也是一种简单命令。赋值语句可以置于其它命令之前,也可以单独成一行。如果置
于命令前,变量将传递给该命令的局部环境(如果这个命令是内置命令或shell函数,除少数例外情况,变量赋值都将起作用)。
如果单独成一行,赋值语句将修改shell状态的值。
当出现一个既不是shell函数又不内置命令的命令名称,Bash会搜索文件系统来寻找同名的可执行文件。PATH环境变量的值是
一些由冒号分隔的目录列表,Bash根据这些目录来搜索文件命令。命令名称中若包含斜划线(或其它目录分隔符),Bash将直
接执行此命令,而不会再搜索。
当一个命令通过PATH搜索得到,Bash将命令名称和对应的完整路径存于哈希表中,PATH搜索之前会查询这个表来提高效率。
如果命令没有被搜到,Bash会执行一个特殊的函数,以命令名称和参数作为这个函数的参数。一些Linux发行版通过这个方式
来提示用户安装缺失的命令。
如果Bash搜索并执行一个文件命令,它会创建一个新的执行环境并fork出一个新的进程,然后让此可执行文件在新的环境中执
![](https://csdnimg.cn/release/download_crawler_static/11227046/bg13.jpg)
行。执行环境完全从shell环境复制而来,只是在信号处理和重定向的文件打开关闭等方面有少许修改。
3.6.4. 任务控制任务控制
shell在前台执行命令时,必须等待命令返回才能执行下一条命令,但shell还可以在后台执行命令,然后立刻读入下一条命
令。任务控制(Job control)指在前台和后台之间切换进程(正在执行的命令),以及挂起(suspend)和恢复(resume)执行的能力。
为实现这个功能,Bash引入了任务(job)的概念,任务本质上指的是被一个或多个进程执行的某个命令。比如,管道为每个管
道元素创建一个进程,它们共同构成一个任务。进程组可以将多个分离的进程组合成单个任务。终端绑定了一个进程组ID,这
同时也是前台进程组的ID。
shell使用一些简单的数据结构来实现任务控制。用一个结构体就能表达一个子进程,包括了进程ID,状态和终止时的返回状
态。管道只不过是这个进程结构体的简单链表。任务(job)的结构更加简单,包括进程列表,一些任务状态(运行,挂起,退
出,等等),以及任务进程组的ID。进程列表通常只有一个进程,只有管道才会导致一个任务包含多个进程。如果一个进程ID
和任务进程组ID相同,则此ID成为这个进程组的领导(leader)。当前任务集合存于一个数组中,这种实现方式在概念上和实际
的表现形式是比较接近的。任务的状态和退出状态由其成员进程的相应状态组合而成。
和shell中其他部分一样,实现任务控制的复杂性在于登记(bookkeeping)。shell必须把进程归入正确的进程组中,并保证子进
程的创建也要同步进行。因为终端的进程组决定了前台任务,终端的进程组必须要设置正确(如果前台任务没有归入shell的进
程组,那么shell将无法读入终端输入)。因为任务如此依赖于进程,实现复杂命令并不是那么显然,比如while和for循环可以作
为一个整体来停止或启动,其他shell很少能做到这一点。
3.6.5. 复合命令复合命令
复合命令包含了一个或多个简单的命令,里面往往还带有一些关键字(比如if或while)。这保证了shell的强大编程能力。
复合命令的实现没有什么特别的地方。解析器为各个命令构造相应的对象,然后遍历这些对象。每个复合命令都使用一个C函
数来实现,它的功能包括执行恰当的展开,执行特定的命令,根据命令的返回值来变更执行流程。以实现for命令的函数为例。
首先它会展开in关键字后面的单词列表,该函数随后遍历这些单词,在for循环中赋给相应的变量并执行。for命令的执行不会受
子命令返回值得影响,但是break和continue这两个内置命令会改变循环的执行过程。一旦列表中所有的单词都已经处理完,for命
令就会返回。从这个例子可以看出来,命令的实现和语法的描述是非常相近的。
3.7. 经验教训经验教训
3.7.1. 什么是重要的什么是重要的
参与到Bash项目中已经有20多年,在这期间我也获益良多。最重要的一点是一定要保留详细的修改日志,其重要性怎么强调
都不过份。通过阅读修改日志来回忆起当初的想法,感觉是很好的。甚至你还可以将某个修改与一个bug报告联系起来,然后
编写一个重现bug的测试用例或提出一些建议。
如果条件允许,我建议在项目之初就考虑全面的回归测试。Bash拥有数千个测试用例,覆盖了差不多所有的非交互性功能。
我考虑过测试交互式功能,其实Posix标准的一致性测试套件中就有交互性测试,只是并没有将这个测试框架发布出来(我认为
很有必要)。
标准很重要,Bash受益于它是一个标准的实现。参与到你正在实现的软件的标准化过程中来也是非常重要的。在讨论相关功
能和行为时,标准往往是最终的参考依据。当然,前提是这是一个好的标准。
外部标准重要,内部标准同样重要。我很幸运地接触到了GNU项目的诸多标准,它们包含了大量关于设计和实现方面的好且
实用的建议。
好的文档同样非常关键。如果你希望别人使用你的软件,全面并清晰的文档就是必要的。一个成功的软件必须拥有大量的文
档,因而开发者提供权威的版本就显得非常重要。
优秀的软件随处可见,那就充分利用起来吧。比如,gnulib中包含了大量的有用的函数,你尽可以把它们"抠"出来。BSD各个
版本和Mac OS X就是这么干的。Picasso说过:好的艺术家靠的是偷,说的就是这个道理。
参与用户社区,但是准备挨骂,有时候这并不好受。活跃的用户社区好处是显然的,但是这些人可能会非常情绪化,不要太当
真就好。
3.7.2. 如果可以重来如果可以重来
Bash拥有数百万用户,我知道后向兼容有多么地重要。在某种意义上,后向兼容意味着永远不用向用户说抱歉。但是,这个
世界远不是这么简单。事实上,我不得不一直做一些破坏兼容性的修改,比如修正一个不好的决定,修改一个错误的设计,或
者更正shell不同部分之间的不兼容性,这些都是情有可原的修改,但是几乎都会引起一些用户的抱怨。我早就应该对当兼容
性分级处理的。
Bash的发展一直都没有特别的开放,我已经习惯于里程碑发布形式(比如 bash-4.2)并由个人提交补丁。我的理由是:我需要
![](https://csdnimg.cn/release/download_crawler_static/11227046/bg14.jpg)
适应开发商们更长的发布周期(相对于自由软件和开源世界),而且我也有过beta版本传播地过于广泛的不快回忆。当然,如果
一切都要重来,我还是会考虑更快的发布频率,比如可以使用一个公开的源码仓库。
不真正动手去做是完不成任何事的。有一件事我已经考虑了很久,却一直没去做,那就是将Bash的解析器重写为递归下降
(recursive-descent)的方式,以取代bison。以前,我以为为了遵守Posix标准,这件事就非得做,但是后来我只需要少量修改
就解决了这个问题。如果当时就从头写起,大概现在我已经实现了一个新的解析器,那么很多问题都会变得简单得多。
3.8. 结论结论
Bash是一个优秀和复杂的大型免费软件。经历了超过二十年的良性发展,已经变得成熟和强大。现在,Bash几乎远处不在,
数百万人每天都在使用,虽然有些人可能还浑然不知。
从Stephen Bourne写出第七版Unix中的shell开始,Bash就一直受到多方的影响。其中最重要的影响来自于Posix标准,它形
成了shell很重要的一部分行为。然而兼容性和标准化难以两全,实现起来是非常头痛的事。
Bash受益于GNU项目,因为GNU项目提供了一个Bash赖以生存的开发生态环境。没有GNU就不会有Bash。Bash还得益于活
跃并朝气蓬勃的用户社区,用户的反馈成就了今天的Bash,这也是自由软件的精髓所在。
术语表术语表
alternation: 某种编程语言中的结构,翻译成替换。在Perl中alternation指一个字符串集合中的任选一个。
word: 指shell中的的一个字符串(以空格等非打印字符分隔),翻译成单词。根据上下文的不同,word具有两种含义。广
义上讲,所有连续的可打印字符串都可以视为word,但具体到shell中,word又可指狭义的不含特殊字符的字符串。两种
含义在文中都有出现,但以后者为主。
expansion: 展开。类似于变量取值,将包含特殊字符和结构的字符序列经过一次或多次替换得到一个新的字符串
pipe/pipeline: 管道, 将一些命令串连起来(用|符号),前一个命令的输出构成后一个命令的输出。另外一种语义是:shell
的内部流程,翻译成流水线。
数据与命令: 文中这两个概念容易混淆。因为所有的shell命令或脚本都是以字符串形式存在,因此都可以视为shell解释
器的输入数据。但是,这些shell命令或脚本从编程语言的角度来看,又是以语句为基本单元的,这样的语句可被称为命
令。而shell中最基本的命令则包括内置命令和文件命令, 从这个意义上看, 一个语句包含了命令和命令的参数,而这些参
数又可视为命令的数据。
primitive: 原语
token: 记号
metacharacter: 元字符
builtin: 内置命令
parser: 解析器
lexical analysis: 词法分析
local: 本地设置
prompt: 提示符
脚注脚注
1. 大部分情况下, 只包含了一个这样的字符.
2. 内置命令exec是这条规则的例外情况
剩余264页未读,继续阅读
![zip](https://img-home.csdnimg.cn/images/20210720083736.png)
![7z](https://img-home.csdnimg.cn/images/20210720083312.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://csdnimg.cn/download_wenku/file_type_ask_c1.png)
![](https://profile-avatar.csdnimg.cn/5482b7aa0778432a9c6a3b31260cd61d_lthcth111.jpg!1)
V打好手里的牌DXP
- 粉丝: 133
- 资源: 3
上传资源 快速赚钱
我的内容管理 展开
我的资源 快来上传第一个资源
我的收益
登录查看自己的收益我的积分 登录查看自己的积分
我的C币 登录后查看C币余额
我的收藏
我的下载
下载帮助
![](https://csdnimg.cn/release/wenkucmsfe/public/img/voice.245cc511.png)
最新资源
- 中国微型数字传声器:技术革新与市场前景
- 智能安防:基于Hi3515的嵌入式云台控制系统设计
- 手机电量低时辐射真增千倍?解析手机使用谣言
- 56F803型DSP驱动的高精度大功率超声波电源控制策略研究
- ARM与GPRS结合的远程监测系统设计
- GPS与RFID技术结合的智能巡检系统设计
- CPLD驱动的低功耗爆炸场温度测试系统设计
- 基于FPGA的智能驱动控制系统:可扩展设计与工业网络协议
- 基于ATmega128和CH374的嵌入式USB接口设计
- 基于AT89C52的温度补偿超声波测距仪:高精度设计与应用
- MSP430F448单片机在交流数字电压表中的应用
- 提升变频器应用效率的12项实用技巧
- STM32F103在数字电镀电源并联均流系统中的应用
- PSpice仿真下的升压开关电源设计:拓扑分析与CCM稳定性提升
- 轻巧高效:MSP430主导的低成本无线传感器网络节点设计
- FPGA在EDA/PLD中实现LVDS接口的应用解析
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
![](https://img-home.csdnimg.cn/images/20220527035711.png)
![](https://img-home.csdnimg.cn/images/20220527035711.png)
![](https://img-home.csdnimg.cn/images/20220527035111.png)
安全验证
文档复制为VIP权益,开通VIP直接复制
![](https://csdnimg.cn/release/wenkucmsfe/public/img/green-success.6a4acb44.png)