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即为调用了。例如: 

wKiom1R-_r3BC-N9AADy6e-tWDM848.jpg

三.bash脚本编程流程控制语句:

    这样了解了脚本编写调用的基本流程之后,可以来了解具体的一些语法了。bash的脚本编程的流程控制语句大致分为:顺序执行;循环执行;选择执行;。变量大致有:本地变量;环境变量;特殊变量;位置参数变量;局部变量。

    流程控制语句之—顺序执行顺序执行顾名思义,就是按照从前向后执行的方式,依次完成脚本的逐个语句。例如:

wKioL1R_CO2AX7RJAADNddlXnf8168.jpg    

流程控制语句之—条件执行:条件执行即为满足一种条件就去执行下一步,不满足就跳过该步不执行。条件判断主要是if型和case型。先来看下if型:

    if格式:if CONDITION; then  CMD ...; fi这是单分支的if语句,即条件满足就执行if中的语句,否则跳过。我们来个例子看一下:

wKioL1R_VgLRAf9NAAClH8ADqBk258.jpg

分析一下: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
    2.字符测试条件:

      符串测试: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,输出用户为普通用户。如果给的用户名不存在,我们给予添加该用户。对于这种题目我们可以先用自己的语言分析一下,先把框架理清楚再下手。

  wKiom1R_NGqTk1_oAAEax5Mnuk4946.jpg

 注意:这里使用了脚本传递参数,就涉及的到特殊变量了。那再来找找脚本中船用的特殊变量有那些。$*表示传递参数的列表,比如外面给了三个参数a,b,c 那么$*就表示{a b c}三个参数的集合。他可以直接用在循环中作为LIST,通常用于定义我们不知道的传递参数列表。$#  表示传递参数的个数,比如给脚本传递10个参数,$#就为10。$1...$n用于表示传递的第几个参数。例如$1表示传递的第一个参数。 

    其实对于这种多分支的if语句,还可以有另外一种简洁方式case语句。case的语法结构:他表示是如果变量引用属于那个pattern,就相应的执行那一个分支。

                                  case 变量引用  in 

          PATTERN1)

  分支1

    ;;

          PATTERN2)

 分支2

    ;;

             ...

          *)

 分支n

    ;;

          esac

     下面我们就用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;


wKiom1R_WROy66SeAAJwJvdYDIc648.jpg



注意:[ -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.

wKioL1R_DVTBfeQpAACCM3lh52o180.jpg    分析: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乘法表看下效果。

wKiom1R_FHjxZYFjAACX_6BxaTI016.jpg    分析一下:外循环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."

~wKiom1R_UUjh0JwlAADE-CID6AQ166.jpg    除此while还可以用于遍历文本的每一行格式如下:while read line; do 循环体 ;done < /path/from/somefile 。按行读取done后面重定向到的文本。例如:我们随便给定一个文本文件,让while统计有多少行。

wKiom1R_VKyBFpinAAB2PbnaJHY058.jpg     until作为循环语句用法和while只用条件判断是相反的,其余的用法相同。即当until条件为假时实现循环。用法简单,朋友可以自行尝试,此处不在举例。下面一篇将是bash的函数的和数组用法。