bash脚本编程:

脚本程序:解释器解释执行

shell: shell是交互式接口;为用户提供了编程环境;能够提供一些内部命令,并且能通过PATH环境变量找到外部命令;把命令提交给内核启动为进程。


编程环境:

    流程控制语句:顺序执行;循环执行;选择执行;

条件测试:真、假

  使用命令: echo $?

命令的状态结果:0: 真 ;1-255: 假


过程式的编程语言的元素:变量、流程、函数、数组


变量:局部变量、本地变量、环境变量、位置参数变量、特殊变量

变量:a)数值型:整型、浮点型、布尔型

      b)字符型:字符串


注:bash变量是弱类型;默认字符型;


变量引用:${VAR_NAME}

  引号:

    弱引用:""

    强引用:''

  命令引用:``

声明某变量为整型变量:

let VAR_NAME=VALUE

declare -i VAR_NAME=VALUE


声明某变量为环境变量:

export VAR_NAME=VALUE

declare -x VAR_NAME=VALUE


脚本的编写格式:

第一行:写明解释器; #!/bin/bash

注释行:所有以#开头的行均为注释行;会被解释器忽略;


执行脚本:

1)赋予执行权限;指明路径执行;

2)直接传递脚本给bash解释器


bash的选项:

-n: 测试脚本中是否有语法 错误;

-x: 调试执行;

算术运算四种方式:

1)$[EXPRESSION]

2)let VAR_NAME=EXPRESSION

3)$((EXPRESSION))

4)$(expr argu1 argu2 argu3)


流程控制之一:for循环

for VAR in LIST; do

STATEMENT1

...

done


循环次数:为列表中的元素的个数;

LIST:列表,包含至少一个元素的字符串集合;

(1) 直接给出;

(2) 数值列表:a){start..end},例如:{1..10}

              b) seq [start [step]] end

(3) 返回列表的命令;

(4) globbing;

(5) 变量引用:$*, $@

例如:添加9个用户,user1,...user9,密码同用户名。

wKiom1SAQ3_StDxYAABx0KSbTso463.jpg

for的几种特殊情况:

(1) for省略,会自动获取脚本参数列表;

(2) C编程风格:

for ((变量赋值;循环条件;修正表达式)); do

CMD1

CMD2

done


(3) 循环嵌套:

for i in [LIST1]; do

CMD1

    for j in [LIST2]; do

CMD2

       ...

    done

done

例如:写一个脚本: ping 172.16.X.Y内的所有主机。(循环嵌套)

wKiom1SAQ7qC_LK9AABfxPtG3kc637.jpg

bash命令退出和退出状态码:

     命令在bash中执行结束退出时,其执行成功与否可通过退出状态码来记录;脚本的退出状态码取决于执行的最后一条命令;

如何自定义退出状态码?

    使用命令:exit # 成功为0;失败则为:1-255


注意:提前退出脚本,也可使用exit命令实现;


bash之条件测试:if/then结构

条件测试:

test EXPRESSION

[ EXPRESSION ]

` EXPRESSION `

COMMAND


测试表达式:

(1) 整数测试; 

(2) 字符串测试;

(3) 文件测试


整数测试:A, B

A -gt B: 大于

A -ge B: 大于等于

A -eq B: 等于

A -lt B: 小于

A -le B: 小于等于

A -ne B: 不等于


字符串测试:A, B

A > B

A < B

A >= B

A <= B

A == B或A = B:等值比较

A != B: 不等于

-z A: 判断A是否为空;空则为真,不空则假;

-n A:判断A是否不空;不空则为值,空则为假;

条件取反:

     ! CONDITION

条件测试语法:

单分支的if语句:

   if CONDITION; then

CMD1

CMD2

...

   fi

例如:传递一个参数给脚本,而后以此参数为用户名,添加此用户;

wKioL1SARi2DpUG6AAA20mOA718925.jpg

if语句嵌套:

   if CONDITION1; then

if CONDITION2; then

CMD

fi

   fi


例如:传递一个参数给脚本,而后以此参数为用户名,不存在时,则添加此用户;

wKiom1SARmvDUj8pAABNwANjt4w842.jpg


双分支的if语句:

if CONDITION-TRUE; then

分支1

else

分支2

fi


例如:写一个脚本:(1) 传递两个整数参数给脚本;(2) 返回其较大一个;

wKioL1SASDTwaxBuAAA_FfADJh8830.jpg

多分支的if语句:

if CONDITION1-TRUE; then

分支1

elif CONDITION2-TRUE; then

分支2

elif CONDITION3-TRUE; then

分支3

...

else

分支n

fi

例如:传递一个用户名参数给脚本:

(1) 如果用户的id号为0,则显示为管理员; 

(2) 如果用户的id号大于6000,则显示为guest; 

(3) 如果用户的id号大于500,则显示为普通用户;

(4) 如果用户的id号大于0, 则显示为系统用户;

(5) 否则,无法识别;

wKiom1SAUbmxZztAAADdL4nOQ44472.jpg

COMMAND用作条件:

(1) 使用命令执行结果;

(a) 使用命令引用

(b) 使用比较操作符

例如:用户UID小于500

方式1、[ `id -u $username` -lt 500 ]

方式2、 userid=`id -u $username`

[ $userid -lt 500 ]


(2) 使用命令的退出状态码

        (a) 先运行命令;

(b) 退出状态码


两种引用方式:

(a) if COMMAND; then

注意:COMMAND不能被命令引用;COMMAND的执行结果通常没有意义,所以其结果通常(&>)被定向至/dev/null

(b) 先执行命令,后判断退出状态码是否为0

   COMMAND

   if [ $? -eq 0 ]


条件测试:

字符串测试:=~

"$A" =~ PATTERN

注:如果变量A中保存的字符串能被PATTERN所匹配;即为真;否则为假;


文件测试:$file

-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是否为同一个文件的硬链接;


  例如:写一个脚本,传递一个文件路径参数给脚本;

(1) 如果脚本无参数,则显示必须给至少一个参数,退出脚本;退出码5;

(2) 路径指向的文件如果不存在,则直接退出脚本;退出码为6;

(3) 判断文件类型:

   (a) 如果是普通文件,则显示为"common file";

   (b) 如果是目录,则显示为"directory";

   (c) 如果是符号链接,则显示为"symbolic link file";

   (d) 如果是块设备,则显示为“block device file";

   (e) 如果是字符设备,则显示为"character device file";

   (f) 否则,则显示为“unkown”;

wKiom1SAXa3DckyfAAFcU5lLnjA238.jpg


组合测试条件:给条件添加逻辑操作符;

或, -o: [ -z "$hostname" -o "$hostname" == 'localhost' ]

与, -a: [ $uid -gt 0 -a $uid -lt 500 ]

非:[ ! EXPRESSION ]

例如:传递一个用户名参数给脚本:

(1) 如果用户的id号为0,则显示为管理员;

(2) 如果用户的id号大于6000,则显示为guest; 

(3) 如果用户的id号大于500,则显示为普通用户;

(4) 如果用户的id号大于0, 则显示为系统用户;

(5) 否则,无法识别;

wKioL1SAWsTQRat3AADZvKAs63s537.jpg

组合测试条件:短路操作符

与:COMMAND1 && COMMAND2

COMMAND1的退出状态如果为假,则COMMAND2不用运行,即可有最终结果;

或:COMMAND1 || COMMAND2

COMMAND1的退出状态如果为真,则COMMAND2不用运行,即可有最终结果;

非:! COMMAND


交互式脚本:

read [OPTIONS] [name ...]

-p "PROMPT"

-t #: 超时时长


给变量以默认值:[ -z "$VAR" ] && VAR=VALUE


case语句:简洁版的多分支if语句;

语法格式:

case 变量引用  in 

PATTERN1)

分支1

;;

PATTERN2)

分支2

;;

...

*)

分支n

;;

esac


注:PATTERN可使用通配符:*:任意长度的任意字符

                         ?: 任意单个字符

                         []:指定范围内的任意单个字符

                         a|b: a或者b

例如:显示如下菜单给用户

cpu) show cpu infomation;

mem) show memory infomation;

disk) show disk infomation;

*) quit

提示用户键入选项:

(1) cpu: 显示CPU相关的信息

(2) mem: 显示内存相关的信息

(3) disk: 列出磁盘设备

(4) 其它任何信息,即为退出脚本

wKiom1SAYJrSoC5fAAE_rubtNO0497.jpg


case语句)

wKiom1SCbmmCOGePAAEGKKNHzv0176.jpg

while、until循环

while循环:

   while CONDITION; do 

循环体

循环控制变量的修正表达式

   done

例如:计算100以内所有偶数之和;

wKiom1SAVOaAtHntAACZS_KnuxQ347.jpg

注:当CONDITION为“真”进入循环,直到“假”退出循环;


until循环:

   until CONDITION; do

循环体

循环控制变量修正表达式

   done

例如:计算100以内所有偶数之和;

wKioL1SAVljjgIZYAACce8777hY357.jpg

注:当CONDITION为“假”时进入循环;为“真”退出循环;

循环控制命令:

break:提前退出循环;

   break [N]: 退出N层循环;N省略时表示退出break语句所在的循环;

continue: 提前结束本轮循环,而直接进入下轮循环;

      continue [N]:提前第N层的循环的本轮循环,而直接进入下轮循环;

语法格式:

while CONDITION; do

CMD1

    if CONDITION2; then

break [N]

    fi

CMD2

...

done


死循环:while)   

              while true; do

                循环体

              done


死循环:until)

             until false; do

               循环体

             done


例如:写一个脚本,判断centos登录了当前系统;

(1) 如果登录了,则脚本终止;

(2) 每5秒种,查看一次用户是否登录;

while)

wKiom1SCcR3iBEnhAACC2eSyEHk518.jpg

until)

wKioL1SCcebgkZ6yAABi6xztLHU147.jpg

while的特殊用法:遍历指定文件的每一行

           while read line; do

              循环体

           done < /path/from/somefile

例如:统计/etc/passwd文件行数;

wKiom1SAZL3RZ25uAABPyTQFe7I478.jpg