1、第四章 Linux Shell 编程,Shell的基本工作原理,在引导系统时,第一个需要运行的进程就是init,PID#1。它衍生出一个getty终端过程, 该过程打开一个终端端口,提供一块空间给标准输入、标准输出和标准错误,并把提示符显 示在屏幕上。 然后执行程序/bin/login。login 程序提示输入密码,加密并验证密码,建立一个初始环境,启动登录shell/bin/bash/etc/passwd 文件的最后一项。bash 寻找系统文件/etc/profile 并执行其中的命令,然后在用户目录下寻找名字叫作.bash_profile 的初始化文件。执行完bash_profile2中的
2、命令以后,就处理用户环境文件中的命令,这个文件通常称 为.bashrc 最后默认提示符美元符号将显示在你的屏幕上,等待你输入命令。,Shell从哪里找到命令,就把该位置赋值给PATH变量,规定光标的基本显示形式。光标将以以下的形式在Shell窗口出现:用户名(u)、符号、主机名(W)及$符号,ulimit命令(Shell内置命令)限制核心文件的最大容量为1 000 000 字节。核心文件是破坏了的 程序文件的转存,而且占用相当大的磁盘空间。,假如用户所属的组名和用户名一致,同时用户id号不大于14,设置umask 为002。若创建的是目录则得到775 的权限,而文件得到664 的权限。否则,u
3、mask 被设为022,此时则给予目录755的权限,或给文件644的权限。,把用户名赋给USER变量(id - un),LOGNAME变量被赋予$USER的值,将到收件箱(储存新邮件)的路径赋给MAIL变量,对于在/etc/profile.d 目录里的所有.sh文件(见14)。 14 检查是否为可执行文件,如果是(见15)。 15 用点命令来执行命令。在/etc/profile.d 目录里的lang.sh 和mc.sh 文件分别设置Linux 的字体和字型,同时还创建一个名为mc的函数,可以用来激活一个名为Midnight Commander 的浏览文件管理程序。 16 done关键字标志fo
4、r循环的结束。,变量,变量类型。有两种类型的变量:局部变量和环境变量。局部变量仅在创建它的Shell 中有效,环境变量则对所有创建它的Shell,局部变量和范围,变量的范围是指变量在一个程序中的什么地方是可见的。对于Shell 而言,局部变量的范围限于创建变量的Shell。 当给一个变量赋值的时候,不要在等号两边留下空格。如果要将一个变量设置为空,在等号后面跟一个换行符即可。 变量前面的美元符号用于提取其存储的值。,定义变量round,定义变量file,当值为含空格的字符串时,必须使用引号。,环境变量,环境变量是能为创建它的Shell 及其派生子进程所用的变量,它们也经常被称为全局变量以区分于
5、局部变量。 一般约定环境变量为大写,它们是那些可以通过内置命令export导出的变量。,设置环境变量,要设置环境变量,必须在给变量赋值或设置了变量后使用export命令。,TERM 变量被赋值为Linux,同时被导出。现在,从本Shell 中启动的进程可以继承该变量了。,TERM变量被定义并导出,这样由本Shell启动的子Shell就可以使用它。,printf 命令,printf命令可以用来格式化输出,其作用是打印格式化的字符串,效果类似C语言的printf函数。 格式包括字符串本身和描述打印效果的字符。定义格式的方法是在%后面跟一个说明符,例如%f 表示后面是一个浮点数,而%d 表示一个整数
6、。,把参数100作为一个小数点后面保留2位的浮点数打印。格式说明符%.2f表示的就是小数点后面保留2位。注意,它跟C语言不同,其参数之间不需要逗号分隔。,把参数100作为一个小数点后面保留2位的浮点数打印。格式说明符%.2f表示的就是小数点后面保留2位。注意,它跟C语言不同,其参数之间不需要逗号分隔。,用printf命令格式化输出Jody字符串和数学表达式的结果,变量扩展修改符(参量扩展),通过特定的修改符,可以检验和修改变量。这些修改符提供了一个快捷的方法来检验变量是不是被设置过,并把输出结果输出到一个变量中。,环境变量EDITOR没有被设置过,修改符“-”用/bin/vi替换变量EDITO
7、R的,因为EDITOR没有被设置,因此打印结果是空,环境变量EDITOR没有被设置过,修改符“-”用/bin/vi替换变量EDITOR的,因为EDITOR没有被设置,因此打印结果是空,子字符串的变量扩展,模式匹配参数用来从字符串的前边或者后边,去掉特定的部分字符串。最常用的方法就是从路径中去点路径名。,%删除变量pathname中匹配模式/bin的smallest trailing portion,也就是删除/bin,%删除变量pathname中匹配模式/bin的largest trailing portion,也就是删除/bin/local/bin。,#删除变量pathname中匹配模式/h
8、ome的smallest leading portion,也就是删除开头的/usr。,#删除变量pathname中匹配模式的largest leading portion,$#variable语句显示赋值给变量name的字符串的字母个数,这里共有18个字母。 位,#!/bin/bash if “$1#*.“ = “tar“ then echo This appears to be a tar. else echo At first glance, this does not appear to be a tar. fi,位置参量,通常情况下,特定的内建变量,被称为位置参量,它们被用于从命令行向
9、脚本传递参数,或者在函数中用于保存传递给函数的参数。这些变量被称作位置参量是因为它们以数字1、2、3区分,这些数字与它们在参量清单中的位置有对应关系。 Shell脚本的名字保存在变量$0 中,位置参量可以被set 命令设置、重置和清空。,数学扩展,Shell 通过运算数学表达式和替换结果来进行数学扩展。在没有双引号和表达式嵌套的情况下,表达式可以被直接处理。 有两种计算数学表达式的格式。,数组,bash 2.x 版本提供了创建一维数组的能力。数组允许你把一串数字、一串名字或者一串文件放在一个变量中。数组的尺寸没有限制,脚标也不必须 是一定顺序的数字。获取数组中某个元素的语法是$arraynam
10、eindex。,$#friend*表示数组的尺寸,即元素个数,$#friend0表示第一个元素的长度。,判断,条件判断语句是几乎所有编程语言中都有的语句,shell中有两种条件判断语句: if表达式 case表达式,if表达式,一般结构if conmmand1then elif command2 then else Ficommand1需要执行并检测其退出状态,如果退出状态为0,则执行其后then与elif之间的语句,同理执行并检测command2的退出状态,并根据退出状态是否为0选择执行elif与else之间或else与fi之间的语句,test命令,shell有一条内部命令test,经常用来
11、在if命令中测试一种或几种条件,其一般格式为:test expression 其中expression表示要测试的条件。test计算expression,若结果为真,其返回的退出状态为0,若结果为假,返回的退出状态就不为零.,返回结果为真,退出状态与 $? 变量,每当程序执行完成后都会给系统返回一个退出状态。该状态是个数值,通常指示该命令运行是否成功。退出状态为0表示运行成功,非零表示运行失败。 Shell自动将最后所执行命令的退出状态设置到shell变量 $? 中,可以用echo命令在终端上显示它的值.,test命令(二),test命令的另一种格式 shell程序使用test命令非常频繁,因
12、此产生了另一种公认的命令格式: expression “”实际上就是命令的名字,同时要求在表达式的有一个配对的“”,在 “之后和”之前都要有空格,“之后和”之前都要有空格,test命令(三),整数操作符 test命令还有一类进行整数比较的操作符,见右表: 例如:操作符“-eq”检测两个整数是否相等,如果有一个变量名为count,想看看它的值是否为0,则可以写成: “$count” eq 0,test命令(四),文件操作符 test提供了一类问询文件 状态的一元操作符,见右表: 例如: -f /etc/fstab 检测fstab文件是否存在且 是否为普通文件,shell入门的拦路虎:syntax
13、 error: unexpected end of file,开始学习bash,每次测试代码都在windows下写好,然后传到linux上执行。 在学习到if 等流程控制的语法的时候,我遇见了第一个难题写的 if 的测试总是不正确:“ if.sh: line 11: syntax error: unexpected end of file ”。 - 这个问题解决方案如下:学shell还是用vi或vim吧! -,shell入门的拦路虎:syntax error: unexpected end of file,dos文件转换成 unix 文件格式 dos 格式 文件 传输到 unix 系统时 ,
14、会在每行的结尾多一个 M , 当然也有可能看不到 , 但是在 vi 的时候 , 会在下面显示此文件的格式 , 比如 “dos.txt“ dos 120L, 2532C 字样 , 表示是一个 dos 格式文件 , 如果是 MAC 系统的 , 会显示 MAC , 因为文件格式的原因有时会导致我们的 unix 程序 , 或者 shell 程序出现错误 , 那么需要把这些 dos 文件格式转换成 unix 格式 , 方法是 vi dos.txt :set fileformat=unix :w 这样文件就转换成 unix 格式 文件了 ,一般在 windows 机器上编写好了文件传到 unix 下就可能
15、会出现这样的情况 , 而一般我们使用 ftp 命令, 常常会加上 bin 参数表示二进制传输, 可是试一下不加 bin 参数 , 可能传到 unix 下就是 unix 格式,Linux Shell /dev/null 2&1解释,shell中可能经常能看到:/dev/null 2&1 命令的结果可以通过%的形式来定义输出 分解这个组合:“/dev/null 2&1” 为五部分。 1: 代表重定向到哪里,例如:echo “123 /home/123.txt 2:/dev/null 代表空设备文件 3:2 表示stderr标准错误 4:& 表示等同于的意思,2&1,表示2的输出重定向等同于1 5:
16、1 表示stdout标准输出,系统默认值是1,所以”/dev/null”等同于 “1/dev/null” 因此,/dev/null 2&1也可以写成“1 /dev/null 2 &1”,Linux Shell /dev/null 2&1解释,那么本文标题的语句执行过程为: 1/dev/null :首先表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,说白了就是不显示任何信息。 2&1 :接着,标准错误输出重定向 到 标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。,if结构应用示例,使用ifthenelse结构编写一个判断命令行所传入参数大小
17、的程序 将所输入数值存放在位置参数$1中 若$1100,则输出:the number is greater than 100 若$110,则输出: the number is smaller than 10 否则输出: the number is between 10 and 100,if表达式应用示例(二),if “$1“ -gt 100 thenecho “the number is greater than 100.“elif “$1“ -lt 10 thenecho “the number is smaller than 10.“ elseecho “the number is bet
18、ween 10 and 100.“ fi 执行 $ chmod +x ifdemo$ ./ifdemo 100 输入数据测试程序功能,循环,shell中提供了可供灵活处理循环的语句,这些循环可以重复执行一组命令,既可以是事先指定的次数,也可以是直到某种条件满足为止。shell中有三个内部循环命令: for while until,for命令,for命令用来将一组命令循环执行预先确定的次数 基本格式: for var in word1 word2wordndocommanddone,不带列表的for命令,for命令也可以写成以下形式: for var do command done shell也
19、能认出这种少了in的特殊格式,shell会自动将命令行键入的所有参数依次组织成列表。,For循环使用总结,在shell环境中,for循环主要有两部分组成,分别为循环列表于操作语句。具体格式如下:for variable in list (循环列表)docommands (操作语句)done,For循环使用总结,其实,在学习这个for循环中,操作语句部分并没有多少的难度。因为这里就是一些unix操作系统的基本语句所构成的。 而其难点就是在于循环列表的确定。因为这个循环列表关系到for循环运行的两个关键参数,一是循环的次数,二是循环内部操作所需要用到的参数值。也就是说,系统工程师只要精通这个循环列
20、表的编写,那么其他内容不会有问题。,如何编制循环列表?,1、 利用文件来作为循环列表。如现在系统工程师需要查找在用户目录下,是否有用户想要的20个文件。这20个文件名字没有什么规律。正常情况下,用户需要一一输入文件名。文件名输入一个,然后查询一次。一共做二十次,显然这操作起来的工作量会非常的大。如果这个文件的数目再增加的话,那么操作起来就会更加的麻烦。,如何编制循环列表?,1、 利用文件来作为循环列表。在for循环中可以解决这个问题。即只需要在循环列表中将这20个文件名字输入进去,然后再操作语句中通过查询操作在指定目录中查找看是否有相关的文件。若有的话,将位置等信息保存到一个文件中。这就可以简
21、化用户的操作,只需要进行一个for循环即可。同时还可以大幅度的缩短查询的时间。,如何编制循环列表?,1、 利用文件来作为循环列表。但是,此时遇到的一个问题就是要在for循环的循环列表中输入20个文件名字。为此系统工程师希望能够将这些文件的名字保存在文件中,然后让for循环从文件中读取这些内容。这即方便,而且也利于后续的调整。其实在for循环中,是支持从文件中读取相关的列表信息。,如何编制循环列表?,1、 利用文件来作为循环列表。大家都知道,这个cat命令的用途主要是从一个文件中读取相关的信息。如果用过管道符的系统工程师,一定知道,可以将某个命令的结果作为另外一个命令的参数。其实,在这里用到的也
22、是这个原理,只是不需要通过管道符来实现而已。具体来说,这个循环列表可以如下定义。 For name in cat filename.txt(注意,由于其读取后变为了文本的内容,为此需要加上单引号)。,如何编制循环列表?,2、 利用系统变量作为循环列表。在Unix操作系统中,for循环最大的特色,莫过于可以利用系统变量来作为循环列表。如还是以查询操作为例,用户可能需要从指定的几个文件夹中查找某个文件。而这几个目录都是有环境变量所确定的。如一个系统工程师刚到企业,接替老的系统工程师的工作。此时这位系统工程师就需要知道Unix服务器系统的一些基本配置。而这其中有一项重要的内容就是一些重要环境变量的设
23、置。,如何编制循环列表?,2、 利用系统变量作为循环列表。如安装了Oracle数据库,就需要知道其安装路径。若安装了Mail服务器,则需要知道其邮件的存储位置与备份位置等等。如果一一去查看这些环境变量,工作量会比较大。如果换成是笔者的话,不会做这么没有效率的事情。笔者的做法是,编写一个for循环,然后将这些重要的环境变量输出到一个文件中进行备份。这将为笔者后续的工作带来很大的方便。如现在笔者想知道Oracle的安装目录与mail的环境变量,就可以利用下面这个循环列表来实现。For varhome in $ORACLE_HOME $MAIL。,如何编制循环列表?,3、 利用通配符作为循环列表。有
24、时候可能系统管理员有这个要求。在某个文件下有多以txt为扩展名的文件。现在系统工程师可能想一一统计这些文件的大小、创建日期、更新日期等等信息,然后将这些统计信息保存到一个文件中。或者想对这些文件进行改名,如在原先的名字之前加上一个backup的字符串,表示这些文件是备份文件。此时,如果一一对这些文件操作的话,会比较麻烦。那么比较合理的做法就是通过for循环来一次性完成这些任务。,如何编制循环列表?,3、 利用通配符作为循环列表。此时,如何书写这个循环列表呢?笔者认为比较合理、比较简便的方法就是通过通配符来实现。如可以利用如下语句来实现:for filename in *.txt。利用这个循环列
25、表,系统就会从目录中查找所有以.txt为扩展名的文件,然后将其为参数,进行后续的操作。如用户需要进行文件更名的话,则可以使用sed操作来对文件进行重命名。在使用通配符作为循环列表中的参数时,最好采用合适的通配符。因为通配符*或者%其含义不尽相同。,while命令,第二种循环命令是while,格式为: while command1do commanddone 先执行command1,并检测其退出状态,如果为0则执行do与done之间命令,再次检测直到command1退出状态不为0 如果第一次执行command1时退出状态就不为0,那么do和done之间的命令可能根本不执行,Shell实例,把某个
26、目录下的文件扩展名改为bat,再以时间为文件名压缩打包存放到某个目录。,把某个目录下的文件扩展名改为bat,再以时间为文件名压缩打包存放到某个目录。 #!/bin/bash for file in $(ls $1) do new_file=$file%.*.bat mv ./$1/$file ./$1/$new_file tmp=$(date) tar cvf ./$tmp.tar ./$1 done,从网上下载一个文件,保存到指定目录,从网上下载一个文件,保存到指定目录 #!/bin/bash url=http:/ dir=/下载 wget -P $dir $url,设计一个Shell程序,
27、在/userdata目录下建立50个目录,即user1user50,并设置每个目录的权限,其 中其他用户的权限为:读;文件所有者的权限为:读、写、执行;文件所有者所在组的权限为:读、执行。,设计一个Shell程序,在/userdata目录下建立50个目录,即user1user50,并设置每个目录的权限,其 中其他用户的权限为:读;文件所有者的权限为:读、写、执行;文件所有者所在组的权限为:读、执行。 #!/bin/bash for (i=1;i=50;i+) do mkdir -p /usrdata/user$i cd /usrdata chmod 754 user$i done,values
28、=(39 5 36 12 9 3 2 30 4 18 22 1 28 25)numvalues=$#valuesfor ( i=0; i numvalues; i+ ); dolowest=$ifor ( j=i+1; j numvalues; j+ ); doif $valuesj -le $values$lowest ; thenlowest=$jfidoneivaluesi=$valueslowestvalueslowest=$tempdonefor ( i=0; i numvalues; i+ ); doecho -ne “$values$it“done echo -e “nn-end-n“,