bash脚本编程详细剖析
背景:bash脚本编程是Linux学习一个至关重要的部分,想完成一个脚本可能很简单;但是想让自己的脚本写的让人觉得心旷神怡实为不简单。bash是所有Linux发行版的几乎都有的,因此我们这里以bash脚本为例,讨论bash脚本的编写方法。对于bash脚本编程中一些比较重要的知识点,我这里也会给予案例演示。
一.脚本编程中前话:
我们都知道,bash脚本编程说白了就是命令的堆积。只不过这种堆积的方式不是杂乱无章的堆积,而是按照一定要求和格式的链接。这说明在学好编程的同时,一定要有大量的的命令基础。这就像我们以前写作文一样,一定要有大量的辞藻基础才能把文章写的饱满。脚本编程其实就是这样,这就需要我们自己多多记忆啦。另外要养成多看好的编程脚本的习惯,好的地方可以记忆下来。久而久之,自己的编程素材就多起来了。
二.bash脚本的的基础知识和流程梗概:
首先,脚本的文本命名为了便于区别,我们都是以.sh作为后缀。脚本的第一行要写明解释器入口例如bash:#!/bin/bash 。要注意除了#!/bin/bash行之外,所有以#号开头的行都是注释行。注释行的文本不作为脚本执行过程。脚本在编写时要注意脚本内容的层次性,各层次之间实现缩进。这样才能让脚本调整的时候一目了然。这也是一个很重要的素质。另外脚本中经常会涉及一些关于计算的表达式,我这里简单提供四种方法,之后会在具体的习题中体现具体如何运用:
1.$[EXPRESSION] $符中括号中加算数表达式的形式。
2.$((EXPRESION))$符加两个小括号加算数表达式的形式。
3.let VAR_NAME=EXPRESSION let 让一个变量等于一个表达式的形式。
4.$(expr argu1 argu2 argu3) $符小括号格式
脚本在编写完之后的调用方式有多种,比如我们调用一个叫files.sh的脚本。我们可以在脚本的相应目录,先给files.sh附加一个执行权限:chmod +x files.sh ,然后在直接这种方式调用 ./files.sh 。或者,我们还可以在命令行中直接:bash -n stusum.sh 先来查看一下有没有语法错误,然后bash files.sh即为调用了。例如:
三.bash脚本编程流程控制语句:
这样了解了脚本编写调用的基本流程之后,可以来了解具体的一些语法了。bash的脚本编程的流程控制语句大致分为:顺序执行;循环执行;选择执行;。变量大致有:本地变量;环境变量;特殊变量;位置参数变量;局部变量。
流程控制语句之—顺序执行:顺序执行顾名思义,就是按照从前向后执行的方式,依次完成脚本的逐个语句。例如:
流程控制语句之—条件执行:条件执行即为满足一种条件就去执行下一步,不满足就跳过该步不执行。条件判断主要是if型和case型。先来看下if型:
if格式:if CONDITION; then CMD ...; fi这是单分支的if语句,即条件满足就执行if中的语句,否则跳过。我们来个例子看一下:
分析一下:for 循环{1..9}。我们用一个单分支语句来条件判断,-le是一个整数测试条件,表示小于等于。我们这里是小于等于5的求和自然是15。对于条件执行语句的条件条件可分三类:(1) 整数测试;(2) 字符串测试;(3) 文件测试
1.整数测试:
整数测试:A, B
A -eq B: 等于
A -ne B: 不等于
A -ge B: 大于等于
A -gt B: 大于
A -le B: 小于等于
A -le B: 不等于
比如想表示条件A < B ,可以写成 if [ $A -lt $B ];then
符串测试:A, B
A > B
A < B
A >= B
A <= B
A == B或A = B:等值比较
A != B: 不等于
-z A: 判断A是否为空;空则为真,不空则假;
-n A:判断A是否不空;不空则为值,空则为假;
例如:我们判断一段未知字符是否为know ;可以表示为if [ "$A" == "keow" ];then
注意:在字符判断是=号与字符之间保留一个空格,避免报错。
以上是单分支的if语句当我们想表示如果不是A,就B的情况是。单分支语句此时就没办法很好的实现了,因此我们还有双分支以及多分支if语句。if CONDITION-TRUE; then CMD1 ;else CMD2 ;fi 。如果多分支格式为:
if TRUE; then
分支1
elif CONDITION2-TRUE; then
分支2
elif CONDITION3-TRUE; then
分支3
...
else
分支n
fi
注意:这里else也可以没有,为了保持格式的统一建议保留。下面给个例子来熟悉下这个单分支和多分支用法。例如:我们要实现以下功能,给脚本传递一个用户名,如果用户存在则判断其id 是否小于500,如果小于500输出,该用户为系统用户。如果id 大于500,输出用户为普通用户。如果给的用户名不存在,我们给予添加该用户。对于这种题目我们可以先用自己的语言分析一下,先把框架理清楚再下手。
注意:这里使用了脚本传递参数,就涉及的到特殊变量了。那再来找找脚本中船用的特殊变量有那些。$*表示传递参数的列表,比如外面给了三个参数a,b,c 那么$*就表示{a b c}三个参数的集合。他可以直接用在循环中作为LIST,通常用于定义我们不知道的传递参数列表。$# 表示传递参数的个数,比如给脚本传递10个参数,$#就为10。$1...$n用于表示传递的第几个参数。例如$1表示传递的第一个参数。
其实对于这种多分支的if语句,还可以有另外一种简洁方式case语句。case的语法结构:他表示是如果变量引用属于那个pattern,就相应的执行那一个分支。
case 变量引用 in
;;
;;
;;
下面我们就用case来实现以下功能。例如:我们使用tar工具把/etc目录备份至/backup目录中,名字为/backup/etc-日期时间.tar.{xz|bz2|gz};
(1) 显示如下菜单
#######################################
# xz) xz compress tool #
# gzip) gzip compress tool #
# bzip2) bzip2 compress tool #
# *) wrong choice and quit #
#######################################
(2) 根据用户选择的工具,执行相应操作;如果用户没有键入任何数据,则默认使用xz;
注意:[ -z $command ] && command="xz" 其实是另一中逻辑判断给条件添加逻辑操作符;而-z则表示判断文件是否为空。具体含义:如果命令为空,执行command="xz" 除此还有[ -z $command ] || command="xz" 它则表示如果command空为真,不执行command="xz" 。怎么样像不像一个双分支if语句。其实在脚本中这种用法代替if非常常见。除此种表示之外,还可以表示为:
或, -o: [ -z "$hostname" -o "$hostname" == 'localhost' ]
与, -a: [ $uid -gt 0 -a $uid -lt 500 ]
非:[ ! EXPRESSION ]
除以上条件判断的种种用法,在条件判断中,有时我们还会用到文件判断。文件判断就是诸如判断一个文件是否存在,判断一个文件是否为目录,判断一个文件是否有写权限等等。
-e $file: 是否存在;存在则为真;
-a $file: 同上;弃用;
-f $file: 文件是否存在,且为普通文件;
-d $file: 是否存在且为目录;
-h $file: 是否存在且为符号链接文件;
-L $file:同上
-b $file: 是否存在且为块设备文件;
-c $file: 是否存在且为字符设备文件;
-S $file: 是否存在且为套接字文件:
-p $file: 是否存在且为管道文件;
-r $file: 当前用户对此文件是否拥有读权限;
-w $file: 当前用户对此文件是否拥有写权限
-x $file: 当前用户对此文件是否拥有执行权限;
-u $file: 文件是否拥有suid权限;
-g $file:文件是否拥有sgid权限;
-k $file: 文件是否拥有sticky权限;
-O $file: 当前用户是否为文件的属主;
-G $file: 当前用户是否属于文件的属组;
-N $file: 文件自从上一次被读取之后,是否被修改过;
$f1 -nt $f2: 文件f1是否比文件f2新;
$f1 -ot $f2: 文件f1是否比文件f2旧;
$f1 -ef $f2: f1和f2是否为同一个文件的硬链接
流程控制语句之一循环执行:循环执行这里有for 型;while 型;until 型;每一种有各自的语法,但是都有和很大的相似之处。先来看一下for 型循环:
for型循环格式:for VAR in LIST ; do STATEMENT1 ...; done。VAR是一个变量,表示LIST中的每一个。LIST是一个列表,LIST的个数决定循环的次数。do ...done 分表用来表示循环体的开始和结束。do ...done之间的部分表示循环体,就是每次循环都会执行的部分。我们来一简单的for循环语句来看一下具体的用法比如:我们给系统添加10个用户分别为:student1...student10.
分析:i变量代表1..10中的每一个数,LIST这里是用花括号{first..last}表示。中间两个点点不可丢掉呀。do表示满足条件开始进入循环,done是循环体的结束位置。那么我们这里来说一下循环的LIST列表的几种生成形式:
1.{first..last} 花括号这种表达方式。
2.seq first step last。这里seq是命令这里使用时要命令引用如:`seq 1 2 100` 这就表示,从1到一百中的奇数部分。
3.globbing通配的方式 例如: for i in /etc/* 表示/etc目录下的所有子文件
4.变量引用的方式:$* ,$# 等
5.直接列出——这是一种最简单粗暴的方式,但是仅限于极少参数的情况。
注意:for 循环如果省略会自动生成列表,但是不提倡这种做法。
for循环的嵌套:有时在循环时我们为了让问题简单的实现,需要使用使用双重或者多重循环。这样就必然会用到循环的嵌套。嵌套其实就是外循环循环一次,内循环循环一个周期的方式。比如我们来实现一个9*9乘法表看下效果。
分析一下:外循环i{1..9}。按照我们说的思路,外循环变化一次,内循环循环一个周期。当i为1时 内循环循环一个周期。循环{1..$i}此时i为1, 实际就是1X1。当i=2时,内循环还是一个周期{1..$i}。此时就是1X1 1X2依次类推。然后只需要在每次i=j的时候换行就可以了,循环中的echo就是实现这一功能的。\t表示退出一个制表符,用-e 来转义。还要注意的是,i ,j都为变量,为了保证输出本身字符时不会报错,均用花括号括起来了。
while型循环格式:while true ;do ... done 这种循环表示当条件为真时开始循环。但是直的注意的是,while循环语句结构中条件判断一旦为真,就进入循环了。而且结构中并没有给予循环跳出条件,因此在写循环时,我们必须给出循环的跳出条件。具体如何实现:比如我们想监控当前登陆系统的一个叫centos的人,每5秒刷新一次。如果登陆就显示centos is logged on. 如果没登陆就显示no login.
#!/bin/bash
#
while true; do
who | grep "centos" &> /dev/null
if [ $? -eq 0 ];then
break
fi
sleep 5
echo "no login"
done
echo "centos is logged."
~
除此while还可以用于遍历文本的每一行格式如下:while read line; do 循环体 ;done < /path/from/somefile 。按行读取done后面重定向到的文本。例如:我们随便给定一个文本文件,让while统计有多少行。
until作为循环语句用法和while只用条件判断是相反的,其余的用法相同。即当until条件为假时实现循环。用法简单,朋友可以自行尝试,此处不在举例。下面一篇将是bash的函数的和数组用法。
转载于:https://blog.51cto.com/guanqianjian/1586148