第十一章 BASH脚本(一)
常见的Shell变量的类型包括环境变量、预定义变量、位置变量、用户自定义变量。本节将分别学习这四种Shell变量的使用。
11. 1、 Shell的环境变量
通过set命令可以查看系统中所有Shell变量(包括环境变量和用户自定义变量),由于内容输出较多,建议使用less命令分页查看。
常用环境变量:
PATH 决定了shell将到哪些目录中寻找命令或程序
HOME 当前用户主目录
HISTSIZE 历史记录数
LOGNAME 当前用户的登录名
USER 当前用户名
UID 当前用名的UID
HOSTNAME 指主机的名称
SHELL 前用户Shell类型
LANGUGE 语言相关的环境变量,多语言可以修改此环境变量
MAIL 当前用户的邮件存放目录
PS1 基本提示符,对于root用户是#,对于普通用户是$
PS2 附属提示符,默认是“>”
例:以分号分隔,显示当前的用户的用户名、宿主目录、登录Shell。
例:查看当前命令的搜索路径,并将/opt/bin目录添加到现有搜索路径中去,从而可以直接执行此目录中的命令。
环境变量的配置文件
用户可在当前的Shell环境中直接为环境变量赋值,但需要长期变更所使用的某个环境变量时,可以修改配置文件。
在Linux系统中,用户环境变量的设置工作习惯上在 /etc/profile 文件及宿主目录中 .bash_profile文件中进行,前者称为全局配置文件(对所有用户起作用),后者称为用户配置文件(允许覆盖全局配置)。
例:在当前用户环境中,将用于限制历史命令的环境变量HISTSIZE的值改为100。
例:编辑“~/.bash_profile”文件,修改PATH的设置,以便用户在下次登录后能够使用服务/opt/bin 目录作为默认的搜索路径。
# vi /root/.bash_profiel
PATH=$PATH:$HOME/bin:/opt/bin
Export PATH
11.2 Shell位置变量
为了在使用Linux程序时,方便通过命令行给程序提供操作参数,Bash引入了位置变量的概念。在执行Shell命令操用时,除了输入的第一字段(命令名或脚本程序名)以外,其余的字符串参数按照从在到右的顺序依次赋组位置变量。
需要引用这些位置变量的值时,采用$n的格式,其中n 是参数位置的序号(从1-9)。例如当执行 servive network restart 命令时,service脚本程序的第1个位置参数“$1”表示,对应的值为network,第2 个位置参数用“$2”表示,对应的值为restart。
$0代表所执行命令或脚本程序的名称,虽然$0与位置变量的格式相同,但是属于预定义变量而不是位置变量。在编写脚本程序时,经常会使用位置变量来接收用户所指定的命令参数。
11.3 预定义变量
预定义变量是由Bash程序预先定义好的一些特殊变量,用户只能使用预定义变量,而不能创建新的预定义变量,或者直接为预定义变量赋值。所有的预定义变量都是由$符号和别一个符号组成的,较常用的预定义变量包括以下这些。
- $#:表示命令行中位置参数的数量
- $*:表示所有位置参数的内容
- $?:表示命令执行后返回的状态,用于检查上一个命令的执行是否正确。在Linix中,命令退出状态为0表示命令执行正确,任何非0值的表示命令执行错误。
- $$:表示当前进程的进程号。
- $!:表示后台运行是最后一个进程的进程号。
- $0:表示当前执行进程的进程名。
预定义变量通常使用在脚本程序中,在命令行界面中的应用并不多见(尽管也可以使用)
11.4、 用户自定义变量
1、定义新的变量
变量名=变量值
例:新建立一个名为“DAY”的变量,初始内容设置为“Sunday”。
# DAY=Sunday
2、查看和引用变量的值
通过在变量名称前添加前导符号$,可以引用一个变量的内容。若需要在终端中输出变量的内容,可以使用echo的命令,echo命令同时也用于显示用户指定的其他字符串内容。
例、查看变量DAY的内容,比较使用符号$与不使用符号$的区别。
当变量名称容易和紧跟其后的其他字符相混淆时,需要添加大括号“{}”将其包围起来,否则将无法确定正确的变量名称。当末指定变量或该变量末设置时,命令将显示空行。
例、在变量DAY的内容后紧跟“zhongxin”字符串并一起显示。
3、为变量赋值的常用方法。
在符号“=”后边直接指定变量内容是为变量赋值的最基本的方法,除此之外,管理员通常还会使用到其他的一些赋值勤操作,从而使变量内容的获取更加灵活多变,以便适应于各种复杂的系统管理任务。常用的几种变量赋值操作包括双引号、单引号、后撇号、read命令。
1)双引号(“)
使用双引号时,允许在双引号的范围内使用$符号来引用其他变量的值(变量引用)。在简单的赋值操作中,双引号有时可以省略。
例、确认变量DAY的内容,并使用双引号为TODAY变量赋值。
2)单引号(‘)
使用单引号时,将不允许在单引号的范围内引用其他变量的值,$符号或者其他任何符号将作为普通字符看待。
例、确认变量DAY的内容,并使用单引号为TODAY变量赋值。
3)反撇号(`)
使用反撇号时,允许将执行特定命令的输出结果赋给变量(命令替换),反撇号内包含的字串必须是能够执行的命令,执行后会输出结果替换该命令字串。新的可替代的语法是$( )。
例、统计当前登录到本地终端中的用户数量,并将结果保存到变量中。
# UserNum=$(w | grep” tty” | wc -l)
例、用一行命令找出安装了fdisk程序的软件包名称(需要先确定fdisk程序的文件位置)。
例、通过find命令找出系统中用户hackli留下的文件或目录,并使用rm命令将其删除。
在需要嵌套使用命令替换操作时,反撇号将力所不能及,这时可以将反撇号用$()来代替。
例、还是使用一行命令,将上一个例子的输出结果保存到变量FdiskPKG中。
4)read命令
除了上述赋值操作外,还可以使用的Bash内置命令read来给变量赋值。read命令可以从终端(键盘)读取输入,实现简单的交互过程。将从标准输入读入一行内容,并以空格为分隔符,将读入的各字段分别赋值给指定列表中的变量(多余的内容赋值给最后一个变量)。若指定的变量只有一个,则将整行内容赋值给该变量。
例、从键盘输入一整行数据,赋值给变量HELO,并确认HELO变量的内容。
例、从键盘输入一整行数据,依次赋值给变量G1、G2,并确认G1、G2变量的内容。
为了使交互式操作的界面更加友好,提高易用性,命令可以结合“-p”选项来设置提示信息,用于告知用户应该输入的内容等相关事项。
例、从键盘读出一个数值变量,并给出相应的提示信息。
4、设置变量的作用范围
对于用户自行定义的变量,默认情况下只能在当前的Shell环境中使用,因此称为局部变量。局部变量在新开启的子Shell环境中是无效的(无法引用定义的变量)。
例、在当前Shell环境中定义一个变量FILESVR,开启一个新的Shell子进程,处于子Shell环境中时将无法使用变量FILESVR的内容。
为了使用户定义的变量在所有的子Shell环境中能够继续使用,减少重复设置工作,可以使用export命令将指定的变量设置为“全局变量”。export命令可以同时使用多个变量名作为参数(不需要使用$符号),变量之间以空格分隔。
例、确认变量FILESVR的内容,并将其设置为全局变量,在子Shell、当前Shell环境中进行验证。
export命令还可以在输出变量的同时对指定名称的变量进行赋值(创建),这样在使用export命令之前就不需要单独为变量进行赋值了。
例、定义两个变量MONTH YEAR,并将其设置为全局变量。
5、清除自定义变量
当用户不再需要使用自定义变量时,可以使用unset命令对已定义的用户变量进行清除,指定一个或多个变量名称作为参数即可(以空格分隔)
例、清除已设置的变量DAY、MONTH、YEAR。
6、数值变量的运算
Bash程序并不适合进行强大的数字运算(如小数、指数等),一般只适合进行简单的整数运算。对Shell变量进行数值运算时,更多的时候是用于脚本程序的过程控制(循环次数等,下一章将会讲解)。对整数数值变量进行算术运算时,可以使用expr表达式命令,格式如下。
Expr 变量1 运算符 变量2 [运算符 变量3] …..
或
$[ 变量1 运算符 变量2 ]
其中,变量1、变量2……对应为需要计算的数值变量(需要用$符号引用),常用的几种运算符如下。
- “+”:加法运算
- “-”:减法运算
- “\*”:乘法运算,注意不能仅使用“*”符号,否则将被当成文件通配符
- “/”:除法运算
- “%”:求模运算,又称为取模运算,即计算数值相除后的余数。
例、设置变量X、Y的值分别为34、12,依次计算变量X、Y的加减乘除及取模运算结果。
# echo $[x+y]
例、计算变量X的值与数值“123”的和,并将计算机结果重新赋值给变量
第十二章 BASH脚本(二)
12.1 Shell脚本的概念
Bash程序不仅可以作为用户Linux管理系统命令的命令操作环境,同时也可以作为一种优秀的脚本程序语言。凡是使用Shell编程语言编写的程序文件都可以称为脚本,通俗一点的说,只要将一些命令行按顺序保存到一个文本文件中,并给予这个文件可执行权限,那么这个文件就可以称为Shell脚本。当然,Shell脚本是为了完成一定的管理任务才创建的,因此Shell脚本文件中各条命令并不是杂乱无章随便放置的,这就需要用户来进行组织和设计了。
写编译型的高级编程语言(如C/C++、Java等)不同,Shell脚本程序是属于解释执行的,并不需要进行特别编译,而只需要有相应的命令解释器即可。长期以来,Shell脚本在Linux系统中得到了广泛的使用,并承担着重要的角色。
12.2 编写Shell脚本文件
1、建立包含可执行语句的文本文件
例、使用编辑器vi编写一个简单的脚本文件repboot.sh,用于报告当前系统中目录所占有的空间大小,并列出其中内核文件的属性信息。
在上述脚本文件中,依次设置了四条可执行命令,两条echo语句用于显示相应的提示信息,而du、ls命令分别用于完成查看目录空间、显示文件属性任务。
2、为脚本文件添加可执行权限
编写并保存好脚本文件后,需要执行该程序才能看到操作结果。但是刚建立的脚本文件通常不具有可执行属性,因此还得使用命令为文件添加“X”权限。
例、为上一步编写的脚本文件添加可执行权限。
将脚本文件设置了可执行属性后就可以通过执行脚本文件来验证程序的正确性了。
12.3执行脚本
在命令行环境中可以有多种方式执行脚本,下面分别进行介绍。
1、直接执行带“X”权限的脚本文件
为脚本文件设置了可执行属性后,在命令千可以直接通过脚本文件的路径执行脚本程序,这也是最常用的一种方式。
例、执行当前目录下的repboot.sh脚本程序文件。
由于脚本文件存放在当前目录,而不是存放在用户的命令搜索路径,因此执行脚本文件时,需要在文件之前加入路径,明确指出需要执行当前目录下的脚本文件,这种方法也是出于对系统安全性的考虑。
2、使用解释器程序执行脚本
这种方式可以将脚本文件作为指Shell定解释器程序(如bash等)的参数,由解释器程序负责读取脚本文件中的内容并执行,因此并不需要脚本文件具有可执行的属性。此方法通常只在脚本的调试阶段使用。
例、使用解释器程序手动加载执行脚本文件中的语句。
12.4 Shell脚本应用实例
本小节主要通过两个编写Shell管理脚本的具体实例,来展示普通Shell脚本的编写应用。
1、Shell脚本案例1
由于公司的文件服务器空间有限,需要完成一项定期任务,即在每周五下班前(17:30)检查公共共享目录 /var/ftp/pub 中的内容,并将其中所有子目录及文件的详细列表和当时的时间信息追加保存到 /var/log/pubdir.log 日志文件中,然后清空该目录中的内容。
根据上述需求描述,推荐的操作步骤如下。
(1)编写脚本文件,并添加执行权限。
(2)设置计划任务并确认服务已经启动。
2、脚本案例2
公司内网开发服务器中的数据库目录位于 /var/lib/mysql ,根据数据安全管理要求,至少每隔三天要做一次完整备份,备份前需要统计该目录占用的总空间大小,并将备分日期、目录空间大小等信息保存到临时文件 /tmp/dbinfo.txt中,然后使用tar命令将dbinfo.txt文件随数据库目录一起备份到 /opt/dbbak 目录中,备份包文件名中要求体现当天的日期。
根据上述需求描述,推荐的操步骤如下。
(1)创建保存备份文件的目录
(2)编写脚本文件,并添加执行权限
(3)设置计划任务并确认服务已经启动
12.5 Shell脚本结构控制语句
Shell作为一种解释型脚本编程语言,同样包含顺序、分支、循环这些基本的程序控制结构。通过使用分支、循环等控制语句,可以编写出应用更加灵活、功能更强大的Shell管理脚本。
12.5.1 使用if 条件语句
在Shell脚本中使用if语句的好处是:可以根据特定的条件来决定是否执行某项操作,当满足不同条件执行不同的操作。本小节分别从条作测试操作、if语句结构、应用实例三个方面来讲解if语句在Shell脚本的应用。
1、条件测试操作
需要在Shell脚本中有选择的性的执行任务时,首先面临的问题就是,如何设置命令执行的条件?
在Shell环境中,可以根据命令执行后的返回状态值来判断该命令是否成功执行,当返回值为0时表示成功执行,否则(非0值)表示执行失败。用于特定条件表达示的测试时,可以使用Liunx系统中提供的专用工具——test命令。
使用test测试命令时,可以有以下两种形式。
Test 条件表达式
或者
[ 条件表达式 ]
这两种方式的作用完全相同,但通常后一种方式更为常用,也更贴近编程习惯。需要注意的是,方括号“[”或者“]与条件表达示语句之间至少需要一个空格进行分隔。
根据需要判断的条件内容不同,条件操作也不同,最常用的条件操作主要包括文件状态测试、比较整数值大小、比较字符串,以及同时判断多个条件时的逻辑关系,下面将分别进行讲解。在后续内容中,主要采用方括号的测试形式,不再对两种形式进行对比。
1)测试文件状态
文件状态测试是指根据给定的路径名称,判断该名称对就的是文件还是目录,或者判断文件是否可读、可写、可执行等。根据所判断的状态不同,在条件表达式中需要使用不同的操作选项。
- -d:测试是否为目录
- -e:测试目录或文件是否存在
- -f:测试是否为文件
- -r:测试当前用户是否有权限读取
- -w:测试当前用户是否有权限写入
- -x:测试当前用户是否可执行该文件
- -L:测试是否为符号连接文件
执行条件测试操作以后,通过预定义变量$?可以获得测试命令的返回状态值,从而能够判断该条件是否成立(返回0值表示条件成立,非0值表示条件不成立)但通过这种方式查看测试结果会比较繁琐。
例、先后测试/etc、/etc/hosts是否是目录,并通过$?变量查看返回状态值,据此判断测试结果。
虽然通过查看$?变量的值也可以判断条件测试结果,但是操作比较繁琐,输出结果也不是很直观。为了更便于查找看条件测试操作的结果,可以结合命令分隔符号&&和命令echo一起使用,当条件成立时直接输出“YES“其中符号&&表示”而且“的关系,只有当前边的命令执行成功后,才会执行后面的命令,否测后边的命令将会被忽略。
例、测试 /media/cdrom及其父目录是否存在,如果存在就显示“YES”,否则不输出任何信息。
例:使用teacher用户登录,并测试是否对 /etc/passwd 文件有读、写权限,如果有则显示“YES”。
需要注意的是测试读取、写入权限时,尽量不要以root用户的身份进行操作,否则可以会看不到预期的效果(因为root是超级管理员,许多文件权限都不受限制)
2) 整数值比较
整数值比较是指根据给定的两个整数值,判断第一个数是否大于、等于、小于……第2个数,可以使用的操作选项如下:
- -eq:第一个数等于第二个数
- -ne:第一个数不等于第二个数
- -gt:第一个数大于第二个数
- -lt:第一个数小于第二个数
- -le:第一个数小于或等到于第二个数
- -ge:第一个数大于或等于第二个数
整数值比较的测试操作在Shell脚本编写中的应用较多,例如,用于判断磁盘使用率,登录用户数量是否超标以及用于控制脚本语句的循环次数等。
例、测试当前登录到系统中的用户数量是否小于或等于10,是则输出“YES”。
例、提取出/boot分区的磁盘使用率,并判断是否超过95%(为了便于理解,操作步骤已适当进行分解)。
3) 字符串比较
字符串比较可以用于检查用户输入,例如,在提供交互式操作时,判断用户输入的选择项是否与指定的变量内容相匹配。“=”、“!=”操作选项分别表示匹配、不匹配,“-z”操作选项用于检查字符串是否为空。其中“!”符号用于取反,表示相反的意思。
例:提示用户输入一个文件路径,并判断是否是 /etc/inittab ,如果是则显示“YES”。
例、若当前环境变量LANG的内容不是“en.US”,则输出LANG变量的值,否则无输出。
例、使用touch命令建立一个新文件test.txt,测试其内容是否为空,向文件中写入内容后,再次进行测试。
4) 逻辑测试
逻辑测试是指同时使用的两个(或多个)条件表达式之间的关系。用户可以同时测试多个条件,根据这些条件是否同时成立或者只要有其中一个条件成立等情况,来决定采取何种操作。逻辑测试可以使用的操作选项如下。
- &&:逻辑与,表示前后两个表达式都成立时整个测试结果才为真,否则结果为假。在使用test命令形式进行测试时,此选项可改为“-a”。
- ||:逻辑或,表示前后两个条件至少有一个成立时整个测试结果才为真,否测结果为假。在使用命令形式进行测试时,此选项可改为“-o”
- !:逻辑否,表示当前指定的条件表达式不成立时,整个测试命令的结果为真。
在上述逻辑测试的操作选项中,“&&”和“||”和通常也用于间隔不同的命令操作,其作用是相似的(前面已经接触过&&符号的应用)。同时使用多个逻辑运算操作时,一般按时从左到右的顺序进行测试。
例、测试当前的用户是否是teacher,若不是则提示 “Not teacher”。
例、只要 /etc/rc.d/rc.local 或者 /etc/init.d/rc.local中有一个是文件,则显示“YES”,否则无任何输出。
例、测试 /etc/profile 文件是否有可以执行权限,若确实没有可执行权限,则提示 No x mode信息。
例、若当前的用户是root且使用的Shell程序是 /bash/bash,则显示“YES”否则无任何输出。
2、if语句的结构
上一小节中学习了常用的一些条件测试操作,实际上使用和&&和||逻辑测试也可以完成简单的判断并执行相应的操作,但是当需要选择执行的命令语句较多时,再使用这种方式将使命令行语句显得很复杂,难以阅读。而使用if语句,则可以更好的体现有选择性执行的程序结构,使得层次分明,清晰易懂。
语句的选择结构是由易到难可以分为三种类型,分别适用于不同的各种场合。
1) 单分支的语句
单分支的if语句是最简单的选择结构,这种结构只判断指定的条件,当条件成立时执行相应的操作,否则不做任何操作。单分支使用的语句格式如下。
if 条件测试命令 ; then
命令序列
fi
在上述语句中,首先通过if判断条件测试命令的返回状态值是否为0(条件成立)如果是,则执行后面的一条或多条可执行语句(命令序列)一直到为fi表示结束;如果条件测试命令在返回状态值不为0(条件不成立),则直接去执行后面的语句(如图12-1所示)。
如图12-1 单分支的if语句结构
在上述格式中,then关键字可以与if写在一行中,其间通过分号“;”进行分隔即可,例如,使用if条件测试命令:then的形式。在Shell环境中,分号“;”也可以作为多条命令的分隔符,使用分号分隔的命令将按照顺序依次被执行。
2)双分支的语句
双分支的if语句使用了两路命令操作,在“条件成立”、“条件不成立”时分别执行不同的命令序列。双分支使用的语句格式如下。
if 条件测试命令
then
命令序列1
else
命令序列2
fi
在上述语句中,首先通过判断条件测试命令的返回状态值是否为0(条件成立),如果是,则执行后面的一条或多条可执行语句(命令序列1),然后跳转至结束判断;如果条件测试命令的返回状态不为0(条件不成立),则执行else后面的语句,一直到fi表示结束(如图12-2所示)
图12-2 双分支的if语句结构
3)多分支的语句
由于if语句可以根据条件测试命令的两种状态分别进行操作,所以能够嵌套使用,进行多次判断(例如,首先判断某学生的得分是否及格,若及格则再次判断是否高于90分……)多重支使用的语句格式如下。
if 条件测试命令1
then
命令序列1
elif 件测试命令2
then
命令序列2
else
命令序列3
fi
上面的语法格式中只嵌套了一个elif语句,实际上语句中可以嵌套多个elif语句,if语句的嵌套在编写shell脚本时并不常用,因为多重嵌套容易使程序结构变得复杂。需要使用多重分支程序结构时,更多的是使用case语句来实现(将在后面小节中讲解)。
使用多分支的语句结构时,会依次对多个条件进行测试,一旦条件不成立时立即退出选择结构,否则将执行相应的命令序列后再跳转至fi,结束判断(如图示12-3所示)。
图12-3 多分支的if语句结构
3、if语句应用系列
if语句是在Shell脚本中使用最多的一种程序结构,本小节将通过脚本实例的方式来展示if选择结构的具体用法。
例、创建脚本chklog.sh,检查文件 /var/log/messages 是否存在,若存在则统计文件内容的行数并输出,否则不做任何操作(合理使用变量,可以提高编写效率)。
例、创建脚本 mkbak.sh,提示用户指定备份目录的路径,若目录已存在则显示提示信息后跳过,否则显示相应提示信息后创建该目录。
例、创建chkuser.sh脚本,统计当前登录到系统中的用户数量,并判断是否超过三个,若是则显示实际数量并给出警告信息,否则列出登录的用户账号名称用所在终端。
例、创建脚本chpostfix.sh,检查postfix进程是否已经存在,若已经存在则输出postfix service is running.;否则检查是否存在 /etc/rc.d/init.d/postfix 可执行脚本,存在则启动postfix服务,否则提示 no postfix script file。
例、编写脚本chkmailsrv.sh每隔五分钟监测一次postfix服务进程的运行状态,若发现进程已终止,则在 /var/log/messages 文件中追加写入日志信息(包括当时时间),并重启postfix服务,否则不进行任何操作。
例:.创建一个shell脚本 /root/program:
--当你输入“kernel”参数时,此shell脚本就返回“user“参数
--当你输入”user“参数时,此shell脚本就返回”kernel”
--当此脚本不输入任何参数或者输入的参数是按照要求输入的话,那么就会输出标准错误“usage:/root/program kernel|user”。
(1).vim /root/program
#!/bin/bash
if [ $# -ne 1 ];then
echo "useage:/root/program kernel|user."
elif [ $1 = "user" ] ;then
echo "kernel"
elif [ $1 = "kernel" ] ;then
echo "user"
else
echo "useage:/root/program kernel|user."
fi
12.5.2 使用for循环语句
在脚本中使用for循环语句时,可以为变量设置一个取值列表,每次读取列表中不同的变量值并执行相关命令操作,变量值用完以后则退出循环。Shell中的for语句不需要执行条件判断,其使用变量的取值来自于预先设置的列表。
1、语句的结构
循环的语句格式如下。
for 变量名 in 取值列表
do
命令序列
done
在上述语句中,使用in关键字为用户自定义变量设置了一个取值列表(以空格分隔的多个值),for语句第一次执行时首先将列表中的第一个取值赋给该变量,然后执行do后边的命令序列;然后 再将列表中的第二个取值赋给该变量,然后执行do后边的命令序列……如此循环,直到取值列表中的所有值都已经用完,最后跳至done语句,表示结束循环(如图12-4所示)。
图12-4 for循环语句的结构
2、for语句应用示例
for语句可以用于对一些特定列表值的处理(如过滤搜索到的文件),本小节将通过脚本实例的方式来展示for循环语句结构的具体方法。
例、依次输出三条文字信息,包括一天中的“Morning”、“Noon”、“Evening”字串。
例、对于使用/bin/bash作为登录Shell的系统用户,检查他们在 /opt 目录中拥有的子目录或文件数量,如果超过100个则列出具体数值及对应的用户帐号。
例、计算 /etc 目录中所有 *.conf 的形式的配置文件所占用的总空间大小。
12.5.3 使用while循环语句
在Shell脚本中使用while循环语句时,将可以根据特定的条件重复执行一个命令列表,直到该条件不再满足时为止。除非有特别需要,否则在脚本程序中应该避免出现无限循环执行命令的情况,因为若无法跳出循环的话,后边的命令操作将无法执行。为了控制循环次数,通常会在执行的命令序列中包含修改测试条件的语句,当循环达到一定次数后,测试条件将不再成立,从而可以结束循环。
1、 while语句的结构
循环的语句格式如下。
while 条件测试命令
do
命令序列
done
在上述语句中,首先通过while判断条件测试命令的返回状态值是否为0(条件成立),如果是则执行do后边的命令序列,然后返回while再次进行条件测试并判断返回状态值,如果条件仍然成立,则继续执行do后边的命令序列,然后返回到while重复条件测试……如此循环,直到所测试的条件不成立时,跳转到done语句,表示结束循环。(如图12-5所示)。
图12-5 while循环语句的结构
使用while循环语句时,有几代人上特殊的条件测试返回值,即true(真)、false(假)。使用true作为测试条件时,条件将永远成立,循环体现内的语句将不会被执行。这两个特殊值也可以用在if语句的条件测试中。
2、 while语句应用实例
while语句可以用于需要重复操作的循环系统管理任务,并能够通过设置循环条件来灵活实现各种管理任务。本小节将通过脚本实例的方式来展示循环结构的具体用法。
例、由用户从键盘输入一个大于1的整数(如50),并计算从1到该数之间各整数的和。
例、批量添加20个系统用户帐号,用户名称依次为stu1、stu2、stu3…..,各用户的初始密码均设置为“123456”。
例、编写一个批量删除用户的脚本程序,将上列中添加的20个用户删 除。
12.5.4其他控制语句
学会条件测试操作以及if、for、while语句的使用以后,编写一般的系统管理任务脚本已经基本可以胜任了。当然,这其中需要大家将之前学习过的各种命令、管道、重定向等操作融会贯通,才能编写出更有效的脚本程序。实际应用中的脚本程序灵活多变,即使是完成同一项任务,也可能有许多种不同的实现方式,大家应该勤加练习,在实践过程中,慢慢去领略。
本节中将简单介绍几个其他的控制语句,以便在编写脚本程序的过程中可以有更多的选择。主要包括case分支语句、until循环、shift移位以及break和continue循环中断语句。
1、 case语句
case语句适用于需要进行多重分支的应用情况,前面使用的多分支if语句,通常都可以修改为case语句,这样程序结构将会更加清晰。
分支语句的格式如下。
Case 变量值
模式1)
命令序列1
;;
模式2)
命令序列2
;;
……….
*)
默认执行的命令的序列
Esac
在上述语句中,将使用case后边的“变量值”与模式1、模式2、……等进行逐一比较(各模式中为用户预设的固定值)直到找到与之相匹配的值,然后执行该模式下的命令序列,当遇到双分号后跳至esac,表示结束分支。如果一直不到与之相匹配的值,则执行最后一个模式*后的命令序列,直到遇到esac后结束分支。(如图12-6)。
图12-6 case分支语句结构
case语句的结构特点
- case行尾必须为单词in,每一模式必须以右括号“)”结束。
- 双分号;;表示命令序列的结束。
- 匹配模式中可以使用方括号表示一个连续的范围,例如[0—9];使用竖杠符号表示或,例如“A|B”(A或者B)。
- 最后的*表示默认模式,当使用前面的各种模式均无法匹配该变量时,将执行*后的命令序列。
例、由用户从键盘输入一个字符,并判断该字符是否为字母、数字或者其他字符,并输出相应的提示信息。
在Linux系统中很多脚本文件都使用了case分支语句,其中 /etc/rc.d/init.d 目录下的各种系统服务脚本最为典型。有兴趣的同学可以通过这些脚本来学习case语句的使用。例如在crond服务脚本的case语句结构中,用于匹配的变量为$1,也就是用户输入的第一个参数。当用户执行 /etc/rc.d/init.d/crond start 命令时,$1的值就是start,因此脚本程序会执行start)模式后边的命令序列,用于启动crond服务。
例:.创建一个shell脚本 /root/program:
--当你输入“kernel”参数时,此shell脚本就返回“user“参数
--当你输入”user“参数时,此shell脚本就返回”kernel”
--当此脚本不输入任何参数或者输入的参数是按照要求输入的话,那么就会输出标准错误“usage:/root/program kernel|user”。
(1).vim /root/program
#!/bin/bash
case $1 in
kernel)
echo user
;;
user)
echo kernel
;;
*)
echo "usage:/root/program kernel|user."
;;
esac
2、 until 循环
until循环语句与while循环语句的结构非常类似,同样是根据特定的条件决定是否执行循环体中的命令序列,只不过while循环语句是当测试条件成立时执行,而until循环是当测试条件成立时结束循环,循环语句的格式如下。
until 条件测试命令
do
命令序列
done
until的含义是“直到……为止”,因此同样会首先执行条件测试并判断其返回值,若条件不成立则执行循环,一直到该测试条件成立时为止,即退出循环.。(如图12-7)
图12-7 until循环语句的结构
大多数时候,通过将测试条件反置,while和until语句可以互相交换。
3、shift语句
shift实际上是Bash中一个特殊的内部命令,但是在命令行较少时用,而更多的是用在Shell脚本程序中,执行shift命令后,位置变量中($1~$9)的命令行参数会依次向左传递。例如:
若当前脚本程序获得的位置变量如下。
$1=file1、$2=file2、$3=file3、$4=file4
则执行一次shift命令操作后(丢弃最左边的一个值),各位置变量的值将变为如下所示。
$1=file2、$2=file3、$3=file4
再次shift执行命令操作后,各位置变量的值将变为如下所示.
$1=file3、$2=file4
合理利用shift语句,可以在位置参数的数量不固定的情况灵活实现程序的功能。
例、编写一个程序,计算多个整数值的和,需要计算的各个数值由用户在执行脚本时作为命令行参数给出。
4、循环控制语句
在使用for、while或until循环语句以及case分支语句的过程中,当满足特定条件时可能会需要中断循环体的执行,或者需要直接转到开关重新判断测试条件(而不再执行后边的命令序列)。这时候,可以使用break和continue语句对执行流程进行控制,这两个语句在与其他大部分编程语言中的含义是类似的。
1) break命令
break即中断的意思,用于跳出当前据的循环体,但是并不退出程序。
例、循环提示用户输入字符串,并将每次输入的内容保存到临时文件 /tmp/input.txt 中,当用户输入“END”字符串时退出循环体,并统计出 input.txt文件中的行数、单词数、字节数等信息,统计完后删除临时文件。
2) continue命令
continue即继续的意思,用于暂停本次循环,跳转至循环语句的顶部重新测试条件。本次执行过程中continue后的命令序列将被忽略。
例、删除系统中的stu1~stu20各用户帐号,但是stu8、stu18除外。
12.6 监控系统脚本例子
例:监控系统硬盘、内存、CPU、登录用户数,超出警戒值则告警。
# /scripts/sys-warning.sh
#监控系统硬盘、内存、CPU、登录用户数,超出警戒值则告警。
#!/bin/bash
#提取系统日期和时间
date=`date`
#提取本服务器的IP地址信息
IP=`ip add show eth0 | awk 'NR==3{print $2}'`
# 1、监控硬盘分区,当使用超过85%的时候发告警:(以下以boot分区为例)
#提取/分区(/dev/sda1)已用的百份比值(只取整数部分)
disk_root=`df -h |awk 'NR==2{print $5}'|awk -F % '{print $1}'`
#设置剩余硬盘容量的告警值为85%,如果当前硬盘使用超过85%,立即告警
if [ $disk_root -gt 85 ]; then
echo "$date,$IP 服务器根分区使用率已经超过85%,请及时处理。" >> /opt/warning.log
echo "$date,$IP 服务器 根分区 使用率已经超过85%,请及时处理。" | mail -s "$IP 服务器硬盘告警" root@example.com
fi
# 2、监控系统cpu的情况,当使用超过80%的时候发告警:
#取当前空闲cpu百份比值(只取整数部分)
cpu_idle=`top -bn 1 |awk NR==3'{print $8}'|cut -d . -f 1`
#设置空闲cpu的告警值为20%,如果当前cpu使用超过80%(即剩余小于20%),立即告警
if (($cpu_idle < 20)); then
echo "$date,$IP服务器cpu剩余$cpu_idle%,使用率超过80%,请及时处理。" >> /opt/warning.log
echo "$date,$IP服务器cpu剩余$cpu_idle%,使用率已经超过80%,请及时处理。" | mail -s "$IP 服务器CPU告警" root@example.com
fi
# 3、监控系统交换分区swap的情况,当使用超过80%的时候发告:
#系统分配的交换分区总量
swap_total=`free -m | grep Swap | awk '{print $2}'`
#当前剩余的交换分区free大小
swap_free=`free -m | grep Swap | awk '{print $4}'`
#当前已使用的交换分区used大小
swap_used=`free -m | grep Swap | awk '{print $3}'`
if (($swap_used != 0)); then
#如果交换分区已被使用,则计算当前剩余交换分区free所占总量的百分比,用小数来表示,要在小数点前面补一个整数位0
swap_per=0`echo "scale=2;$swap_free/$swap_total" | bc`
#设置交换分区的告警值为20%(即使用超过80%的时候告警)。
swap_warn=0.20
#当前剩余交换分区百分比与告警值进行比较(当大于告警值(即剩余20%以上)时会返回1,小于(即剩余不足20%)时会返回0 )
swap_now=`expr $swap_per \> $swap_warn`
#如果当前交换分区使用超过80%(即剩余小于20%,上面的返回值等于0),立即发邮件告警
if (($swap_now == 0)); then
echo "$date,$IP服务器swap分区只剩下 $swap_free M 未使用,剩余不足20%,使用率已经超过80%,请及时处理。" >> /opt/warning.log
echo "$date,$IP服务器swap分区只剩下 $swap_free M 未使用,剩余不足20%,使用率已经超过80%,请及时处理。" | mail -s "$IP 服务器内存告警" root@example.com
fi
fi
# 4、监控系统用户登录的情况,当用户数超过3个的时候发告警邮件:
#取当前用户登录数(只取数值部分)
users=`uptime | awk '{print $4}'`
#设置登录用户数的告警值为3个,如果当前用户数超过3个,立即发邮件告警
if (($users >= 3)); then
echo "$date,$IP 服务器用户数已经达到$users个,请及时处理。" >> /opt/warning.log
echo "$date,$IP 服务器用户数已经达到$users个,请及时处理。" | mail -s "$IP 服务器用户数告警" root@example.com
fi
二、加入任务计划:每十分钟检测一次。
# chmod a+x /scripts/sys-warning.sh
# crontab -e
*/10 * * * * /scripts/sys-warning.sh
# service crond restart
第十三章 配置BASH环境
13.1 Shell的环境变量
这里所说的环境变量是指用户登录后Linux系统预先设好的一类Shell变量,其功能是设置用户的Shell工作环境,包括用户的宿主目录、命令查找路径、用户当前的目录、登录的终端等。在实际使用过程中,环境变量并没有严格的区分定义,用户自己设置的变量也可以作为环境变量。
环境变量的名称比较固定,通常使用大写字母、数字和其他字符组成,而不使用小写字母。环境变量的值一般由Linux系统自行维护,会随着用户状态的改变而改变,用户可以通过读取环境变量来了解自己的当前状态。
1、查看环境变量
通过set命令可以查看系统中所有Shell变量(包括环境变量和用户自定义变量),由于内容输出较多,建议使用less命令分页查看。
常用环境变量:
PATH 决定了shell将到哪些目录中寻找命令或程序
HOME 当前用户主目录
HISTSIZE 历史记录数
LOGNAME 当前用户的登录名
USER 当前用户名
UID 当前用名的UID
HOSTNAME 指主机的名称
SHELL 前用户Shell类型
LANGUGE 语言相关的环境变量,多语言可以修改此环境变量
MAIL 当前用户的邮件存放目录
PS1 基本提示符,对于root用户是#,对于普通用户是$
PS2 附属提示符,默认是“>”
例:以分号分隔,显示当前的用户的用户名、宿主目录、登录Shell。
例:查看当前命令的搜索路径,并将/opt/bin目录添加到现有搜索路径中去,从而可以直接执行此目录中的命令。
2、环境变量的配置文件
用户可在当前的Shell环境中直接为环境变量赋值,但需要长期变更所使用的某个环境变量时,可以修改配置文件。
在Linux系统中,用户环境变量的设置工作习惯上在 /etc/profile 文件及宿主目录中 .bash_profile文件中进行,前者称为全局配置文件(对所有用户起作用),后者称为用户配置文件(允许覆盖全局配置)。
例:在当前用户环境中,将用于限制历史命令的环境变量HISTSIZE的值改为100。
例:编辑“~/.bash_profile”文件,修改PATH的设置,以便用户在下次登录后能够使用服务/opt/bin 目录作为默认的搜索路径。
# vi /root/.bash_profiel
PATH=$PATH:$HOME/bin:/opt/bin
Export PATH
注意:登录的几个脚本文件
/etc/profile
/etc/bashrc
~/.bash_profile
~/.bashrc
~/.bash_logout
每一个文件都有特殊的功用并对登陆和交互环境有不同的影响。
/etc/profile 和 ~/.bash_profile 是在启动 一个交互登陆shell的时候被调用。
/etc/bashrc 和 ~/.bashrc 是在一个交互的非登陆shell启动的时候被调用。
~/.bash_logout 在用户注销登陆的时候 被读取
一 个交互的登陆shell会在 /bin/login 成功登陆之后运行。一个交互的非登陆shell是通过命令行来运行的,如[prompt]$/bin/bash。一般一个非交互的shell出现在运行 shell脚本的时候 。之所以叫非交互的shell,是因为它不在命令行上等待输入而只是执行脚本 程序。
3、命令别名
Bash中的命令别名功能可以将频繁使用的复杂命令定义为简短的别名,当用户需要执行该条复杂命令时,只需要使用别名即可完成对应的操作,降低了操作复杂性,提高了输入较率,别名功能主要通过这两个命令进行设置和取消。
1) 查看已设置的别名
直接执行alias命令可以查看当前用户环境中已设置的所有命令别名,若只需要查看待定的命令别名,可以使用别名名称作为参数。
在系统中默认情况下已经定义好了几个别名,例如,ll是ls -l命令的别名,这样用户在需要以长格式显示文件信息时,就不需要每次都输入ls -l命令,直接输入ll命令即可。
2) 设置命令别名
用户可以根据自己的使用习惯定义特定的命令别名,具体格式可参考alias命令的输出结果,基本的形式为:alias别名=’实际命令’
3) 取消已设置的命令别名
需要取消系统中定义的命令别名时,可以使用unalias命令。使用别名名称作为参数可以删除特定的命令别名,结合-a选项使用时将删除所有已定义的命令别名。
使用alias命令定义新的别名或使用unalias命令取消已定义的别名都只在用户的当前Shell环境中有效,下次启动后将恢复初始的别名设置。因此对于用户经常使用的命令别名,应该将相应的别名设置命令添加到宿主目录的 .bashrc文件中。
在系统server0和desktop0上创建自定义命令为psa,
此自定义命令将执行/bin/ps -aux,
此命令对系统中所有用户有效。
[root@server0 ~]# Vim /etc/bashrc
alias psa='/bin/ps -aux'
[root@server0 ~]# srource /etc/bashrc