位置: 编程技术 - 正文
今天木有冷笑话,只有一个噩耗。噩耗是:今天木有冷笑话!!!不要总想着冷笑话嘛,有点追求,听毛主席的话:好好学习,天天向上!
第七章输入输出、文件与命令执行
学C的应该了解标准输入输出和错误输出吧?感觉总打很多字进度太慢,所以一直在省略类似C的东西,也方便以后看这篇文章的人能够快速学完shell脚本(或者是快速看完这本书)。
读取行read命令是重要方式之一,它可以自标准输入读取行后,通过shell字段切割的功能(使用$IFS)进行切分,第一部分给第一个变量,第二部分给第二个,类推。如果切割单词多余变量,则剩下所有的给最后一个变量。如果输入行以反斜杠结尾,则read会丢弃反斜杠与换行符继续读取下行数据。它有一个选项 -r,它将忽视最后反斜杠当读入数据。使用read可能的一个错误是通过循环让read读取一个文件如:while IFS=: read user pass uid gid fullname homedir shell < /etx/passwd ... 这个循环将一直下去并且每次read只读passwd的第一行。因为每次循环都重新打开了passwd文件读取内容。解决办法是: cat /etc/passwd | while IFS=: read ....... 这样通过管道解决读取文件问题。 这里有一个概念,文件描述符,一般这个文件描述符是由0-9这几个数字来描述的,所以传统上shell也就允许你最多打开十个文件。比如make 1> results 2> ERRS 。命令make的标准输出(文件描述符1)传给results,并将错误输出(文件描述符2)传给ERRS。设置完文件描述符后,如何引用呢?像这样:make > results 2>&1 。1> results这里的1其实没必要,供输出重定向的默认文件描述符是标准输出,也就是文件描述符1,重定向 > results让文件描述符1作为文件results,接下来重定向2>&1有两部分,2>重定向文件描述2,也就是标准错误输出。而&1就是刚才我们的疑问,用来引用我们定义的文件描述符。特别注意:2>&1这样的四个字符一定要连着写。
再介绍一个用来改变shell本身I/O设置的exec命令。如果只有I/O重定向而没有任何参数时,exec会改变shell的文件描述符:
上例展示了如何关闭文件描述符。exec还有一个功能就是在当前shell下执行指定的程序。
书中对printf做了完整的介绍,这里就不再介绍了,就是C里边的那些东西。
shell中有两种与文件名相关的展开:第一个是波浪号展开(~ tilde expansion),另一个叫法较多如通配符展开式(wildcard expansion)、全局展开(globbing)或路径展开(pathname expansion)。
如果命令行字符第一个字符为波浪号或者变量指定的值里任何未被引号括起来的冒号之后的第一个字符为波浪号时,shell便会执行波浪号展开。波浪号展开的目的,是要将用户根目录的符号型表示方式,改为实际的目录路径。
shell环境下的通配符展开,有几个基本的通配符:? , * , [set] [!set] ,前俩略过,第三个是匹配出现中括号集合中的字符,第四个取反义。比如可以查找 *.html 就知道处所有类似的文件。另外有一点注意的,在linux下文件名里的.号没有任何特殊意义,匹配所有文件时只需用一个*即可,不用像windows下那样*.*。习惯上,当执行通配符展开时,shell会忽略文件名开头为一个点号的文件。像这样的点号文件(dot files)通常用作程序配置文件或启动文件。
命令替换,书上写的概念很绕口,其实就是一个命令的用法或者写法,例如:echo outer `echo inner1 `echo inner2 ` `这样输出结果就是 outer inner1 inner2 类似命令嵌套,从最内层开始执行。注意是反单引号,键盘1左边与波浪号同键那个。但是这样嵌套多了之后会非常难以阅读,就出现了新的语法:$ echo outer $(echo inner1 $(echo inner2) inner1) outer这样输出结果就是outer inner1 inner2 inner1 outer。这样清晰多了。
书中教了一个expr命令,从提到这个命令,到接下来的两段都在说这个命令不好用,并且可以是由$(( )) ,test替代。但是可以了解一下,作用就是计算之后跟着的一个表达式比如:expr 1 + 1 。这里注意加号两边的空格,是必要的,书里貌似没说,郁闷半天才发现必须添加空格。。- -!
这里又提了引用,就是说用来防止shell将某些东西解释成你不想要的意义,比如你就是有就想要*,而不是需要一个通配符,这时候你需要转义() ,或者是单引号引起来(单引号引起来的内容转义符号也无效),或是双引号。混用的时候请小心。
书中详细说了一下命令的执行顺与,感觉很有必要细看一下,全都摘录一下。shell从标准输入或脚本中读取的每一行成为管道;它包含了一个或多个命令,这些命令被零或多个管道字符隔开。事实上还有很多特殊符号可用来分隔单个的命令:分号;、管道|、&、逻辑AND&&、还有逻辑OR||。对于每一个读取的管道,shell都会将命令分割,为管道设置I/O,并且对每一个命令依次执行下面操作:1、将命令分割成token,是以固定的一组meta字符分隔,有空格、制表符、换行字符、;、(、)、<、>、|、和&。token的种类包括单词、关键字、输出入重定向器,以及分号。这是微妙的,但是变量、命令还有算符替换,都可以在shell执行token认定的时候被执行。这就是为什么先前所举例子vi ~$user/.profile中波浪符号可以展开像预期的那样工作。2、检查每个命令的第一个token,看看是否它是不带有引号或反斜杠的关键字。如果它是一个开放的关键字(if 或者 { (之类的),则这个命令其实是一个复合命令。shell为复合命令进行内部的设置,读取下一条命令,并再次启动进程。如果关键字非复合命令的开始符号(例如,它是控制结构的中间部分,像then、else或do,或是结尾部分,例如fi,done或逻辑运算符),则shell会发出语法错误的信号。3、将每个命令的第一个单词与别名列表对照检查。如果匹配,它便代替别名的定义,并回到步骤1;否则,进行步骤4(别名是给交互式shell使用)。回到步骤1,允许让关键字的别名被定义:例如alias aslongas=while or alias procedure=function。注意,shell不会执行递归的别名展开,反而当别名展开为相同的命令时它会知道,并停止潜在的递归操作。可以通过引用要保护的单词的任何部分而禁止别名展开。4、如果波浪号字符出现在单词的开头处,则将 波浪号替换成用户的跟目录$HOME,将~user替换成user的根目录。波浪号替换会发生在下面的位置:* 在命令行里,作为单词的第一个未引用字符* 在变量赋值中的=之后以及变量赋值中的任何:之后* 形式${ varibale op word } 的变量替换里的word部分5、将任何开头为$符号的表达式,执行参数(变量)替换。6、将任何形式为$(string)或 `string`的表达式,执行命令替换。7、执行形式$((string))的算术表达式。8、从参数、命令与算术替换中取出结果行的部分,再一次将它们切分为单词。这次它使用$IFS里的字符作为定界符,而不是使用步骤1的那组meta字符。通常,在IFS里连续多个重复的输入字符是作为单一定界符,这是你所期待的。这只有对空白字符而言是真的。对非空白字符,则不是这样的。举例说,当读取以冒号分隔字段的/etc/passwd文件时,两个连续冒号所界定的是一个空子段。9、对于*、?以及一对[...]的任何出现次数,都执行文件名生成的操作,也就是通配符展开。、使用第一个单词作为一个命令,遵循查找次序,也就是,先作为个特殊的内建命令,接着是作为函数,然后作为一般的内建命令,以及最后作为查找$PATH找到的第一个文件。、在完成I/O重定向与其他同类型事项之后,执行命令。
shell程序碰到一句命令,都会执行上边的一次流程。比如:
命令一开始会根据shell语法分割token,最重要一点是I/O重定向 > out 在这里是被识别的,并存储供稍后使用。最后这句echo被分为5个token,分别是: echo ,~+/${f}[] , $y , $(echo cmd subst) ,$((3 + 2))这5个部分。然后检查第一个单词echo是否为关键字,例如if或for,这里不是所以命令不变继续处理。检查第一个单词依然是echo 是否为别名,这里不是,继续执行。扫描所有单词是否需要波浪号展开,本例中,~+为ksh与bash的扩展,等同于$PWD,也就是当前的目录。token 2将被修改变成如下:echo /tmp/x/${f}[] $y $(echo cmd subst) $((3 + 2))下一步变量展开:token2与token3被修改变成:echo /tmp/x/f[] a b $(echo cmd subst) $((3 + 2))再来要处理的是命令替换。注意,这里可递归引用列表里的所有步骤!在次例中,因为我们试图让所有的东西容易理解,因此命令替换修改了token4,结果如下:echo /tmp/x/f[] a b cmd subst $((3 + 2))现在执行算术替换,结果如下:echo /tmp/x/f[] a b cmd subst 5前面所有的展开产生的结果,都将再一次被扫描,看看是否有#IFS字符,如果有则它们是作为分隔符,产生额外的单词。最后的替换阶段是通配符展开变化如下:echo /tmp/x/f1 /tmp/x/f2 a b cmd subst 5这时,shell已经准备好要执行最后的命令了。它会去寻找echo。正好ksh与bash里的echo都已内建到shell中。shell实际执行命令。首先执行>out的重定向,再调用内部的echo版本。这一系列完成了这句语句的执行。
eval语句是再告知shell取出eval的参数,并再执行它们一次,是他们经过整个命令行的处理步骤。看一个例子:
运行之后你会发现shell把|与more看作ls的参数,而不是直接产生一页页的文件列表。这是由于在shell执行变量时,管道字符出现在步骤5,也就是在它确实寻找管道字符之后(在步骤1).变量的展开一直要到步骤8才进行解析。结果,shell把 | 与more看作ls的参数,使得ls会试图在当前目录下寻找名为|与more的文件。
现在,想想eval $listpage吧,在shell到达最后一步时,会执行带有ls、|与more参数的eval,这会让shell回到步骤1,具有一行包括了这些参数的命令。在步骤1发现|后,将该行分割为两个命令:ls 和more。每个要被处理的命令都以一般方式执行,最后的结果是在当前目录下分页的文件列表。
还有两个其他的结构,有时也很有用,subShell与代码块。subShell是一群被括在圆括号里的命令,这些命令会在另外的进程中执行。当你需要让一小组的命令在不同的目录下执行时,这些命令会在另外的进程中执行。如:tar -cf - . | (cd /newdir; tar -xpf - )左边tar产生当前目录打包文件,将它传送给标准输出。右边的cd命令会先切换到新目录,也就是让打包文件在此目录下解开。然后,右边的tar将从打包文件里解开文件,请注意,执行此管道的shell并未更改它的目录。
代码块概念上与subShell雷同,只不过它不会建立新进程。代码块用花括号括起。且会对主脚本造成影响(比如当前目录)。一般花括号被视为关键字,即它们只有出现在命令的第一个符号时被识别。实际上,这表示你必须将结束花括号放置在换行符或分号之后。
shell有很多命令,之前说过,特殊内建命令与一般内建命令的差别在于shell查找要执行的命令时,会先查找特殊内建命令再找shell函数,接下来才是一般内建命令。最后是$PATH路径内的外部命令。这种查找顺序让定义shell函数以扩展或覆盖一般shell内建命令成为可能。举例说你希望shell的提示号能包含当前目录路径的最后一个组成部分。最简单的实现方式,就是在每次改变目录时,都让shell改变PS1.你可以写一个自己专用的函数如下:
这么做有个问题,你必须在shell下输入chdir而不是cd,这样你可以自己写一个名为cd的函数,然后shell会先找到你的cd函数,而不是一般内建函数cd。但是这样又会有问题,shell函数如何真正访问cd命令,这里函数内cd会再此调用你写的cd函数导致递归出现。这时候我们需要转义策略,使用内建命令command来告诉shell要避开函数的查找直接访问真正的命令。
#cd --改变目录时更新PS1的私人版cd(){command cd "$@"x=$(pwd)PS1="${x##*/}$ "}
第八章产生脚本
详细讲了一下set命令。。。
这一章详解了两个好用脚本的实现过程,这两个脚本详解内容揉杂在注释里给出。
脚本一:功能是在给出的路径下查找目标路径
这里作者给留了课后作业:增添一个功能,不单单只能匹配文件,也能匹配其他东西比如:符号性连接文件,可读取文件或者可执行文件之类的,需要test -x选项来进行匹配,本人完成的如下:
欧了,课后作业完成,安全性还没经验,有老师批改作业木有?
下边给了第二个脚本,是软件构建自动化,代码灰长长。。。这个打代码都码的头疼了,跳过吧,有兴趣童鞋自己搞定。我是不求甚解的先赶进度了,回来再搞。
个人原创,转载请注明:三江小渡
推荐整理分享shell脚本学习指南[三](Arnold Robbins & Nelson H.F. Beebe著)(shell脚本 教程),希望有所帮助,仅作参考,欢迎阅读内容。
文章相关热门搜索词:shell脚本指南,shell脚本总结,shell脚本基础教学,shell脚本应用实战,shell脚本应用实战,shell脚本 教程,shell脚本学习指南,shell脚本基础教学,内容如对您有帮助,希望把文章链接给更多的朋友!
shell脚本学习指南[四](Arnold Robbins & Nelson H.F. Beebe著) 回忆起一件事情:之前用linux寻找中文输入法的时候,在百度输入了fcitx,然后结果上边有个,您要找的是不是:讽刺腾讯。本来一直记不住这个输入法名
shell脚本学习指南[五](Arnold Robbins & Nelson H.F. Beebe著) 作者告诉我们:到目前为止基础已经搞定,可以将前边所学结合shell变成进军中等难度的任务了。激动的要哭了,终于看到本书结束的曙光了TT。码字比
shell脚本学习指南[六](Arnold Robbins & Nelson H.F. Beebe著) 学shell到现在了,一直以为自己不会犯一个大家常说的非常二的问题,结果这本书最后的时候犯了个十分2的事,晚节不保啊!!!我在测试文件路径下
标签: shell脚本 教程
本文链接地址:https://www.jiuchutong.com/biancheng/374762.html 转载请保留说明!上一篇:shell脚本学习指南[二](Arnold Robbins & Nelson H.F. Beebe著)(shell脚本基础教学)
下一篇:shell脚本学习指南[四](Arnold Robbins & Nelson H.F. Beebe著)(shell脚本指南)
友情链接: 武汉网站建设