bash脚本编程:之case语句

知识回顾 条件测试: 0: 成功 1-255: 失败

命令: 判断 [ expression ] [[ expression ]] test expression

exPression: 整数测试: -gt, -ge, -lt, -le, -eq, -ne 字符串测试:

, < , >=, <=, ==, !=, =~, -z是否为空, -n是否非空 文件: -e, -f, -d, -b, -c, (-h|-L)是否为链接文件, -S, -s测试文件大小的, -a, -p, -r, -w, -x

多分支的if语句: if boolean_expression1; then suite1 elif boolean_expression2; then suite2 ... elif boolean_expressionn; then suiten else else_suite fi

练习:写一个脚本,接受如此格式 script.sh {start|stop|restart|status} 1、如是start,那么创建/var/lock/subsys/script.sh,显示启动成功; 2、如果参数是stop,则删除/var/lock/subsys/script.sh,显示停止成功 3、如果restart,则删除,再创建,显示成功; 4、如果status, 如果文件存在,则显示running,否则,显示stopped 我们写脚本,最好写一步测一步 #!/bin/bash

myService=basename $0 lockFile="/var/lock/subsys/$myService"

[ $# -lt 1 ] && echo "Usage: $myService {start|stop|restart|status}" && exit 4

if [ "$1" == 'start' ]; then touch $lockFile echo "Starting $myService OK" elif [ "$1" == 'stop' ]; then rm -f $lockFile echo "Stopping $myService OK" elif [ "$1" == 'restart' ]; then rm -f $lockFile touch $lockFile echo "Restarting $myService OK" elif [ "$1" == 'status' ]; then if [ -f $lockFile ]; then echo "$myService is running" else echo "$myService is stopped" fi else echo "Usage: $myService {start|stop|restart|status}" exit 3 fi

取代多分支语句if的格式就是case的方法 case语句的语法格式: case expression in pattern1) suite1 ;; pattern2) suite2 ;; ... patternn) suiten ;; *) other_suite ;; esac

所以上一个多分支语句就可以写出这个样子 #!/bin/bash

myService=basename $0 lockFile="/var/lock/subsys/$myService"

[ $# -lt 1 ] && echo "Usage: $myService {start|stop|restart|status}" && exit 4

case $1 in 'start') touch $lockFile echo "Starting $myService OK" ;; 'stop') rm -f $lockFile echo "Stopping $myService OK" ;; 'restart') rm -f $lockFile touch $lockFile echo "Restarting $myService OK" ;; 'status') if [ -f $lockFile ]; then echo "$myService is running" else echo "$myService is stopped" fi ;; *) echo "Usage: $myService {start|stop|restart|status}" exit 3 ;; esac

case中各pattern可以使用模式: a|b: a或者b 类似于我们的文件名通配 *:匹配任意长度的任意字符; ?:匹配任意单个字符; [-]:范围匹配 [a-z]) [0-9])

练习:写一个简单脚本 1、提示用户输入一个任意字符; 2、能判断此字符是数字、字母或特殊字符;

#!/bin/bash

while true; do read -p "Enter a char: " char

[[ "$char" == 'quit' ]] && break

case $char in [a-z]) echo "letter" ;; [0-9]) echo "digit" ;; *) echo "special" ;; esac done

练习:写一个脚本,能对/etc/目录进行打包备份,备份位置为/backup/etc-日期.后缀 1、显示如下菜单给用户: xz) xz compress gzip) gzip compress bip2) bzip2 compress 2、根据用户指定的压缩工具使用tar打包压缩; 3、默认为xz;输入错误则需要用户重新输入;

#!/bin/bash

[ -d /backup ] || mkdir /backup cat << EOF Plz choose a compress tool: xz) xz compress gzip) gzip compress bip2) bzip2 compress EOF

while true; do read -p "Your option: " option option=${option:-xz}

case $option in xz) compressTool='J' suffix='xz' break ;; gzip) compressTool='z' suffix='gz' break ;; bzip2) compressTool='j' suffix='bz2' break ;; *) echo "wrong option." ;; esac done

tar ${compressTool}cf /backup/etc-date +%F-%H-%M-%S.tar.$suffix /etc/*

练习:写一个脚本,完成如下功能 说明:此脚本能够为指定网卡创建别名,指定地址;使用格式:mkethalias.sh -v|--verbose -i|--interface ethX 1|-i选项用于指定网卡; 2、如果网卡存在:在命令行,请用户指定一个别名; 3、让用户指定IP和掩码; 4、用户可以同时使用-v或--verbose选项:如果使用了,则在配置完成后,显示配置结果;否则,则不予显示;

#!/bin/bash

debug=0

while [ $# -ge 1 ]; do case $1 in -i|--interface) ethcard="$2" shift 2 ;; -v|--verbose) debug=1 shift ;; *) echo "Wrong options or arguments." echo "Usage: basename $0 [-v|--verbose] -i|--interface Interface" shift $# ;; esac done #这里的shift是为了剔除变量,让while判断参数个数是否是小于1的,不踢出的话,会形成一个死循环的

一定要理解shift 1,shift 2的用途是什么,结合while [ $# -ge 1] 去想

echo "Interface: $ethcard , Verbose Flag: $debug "

! ifconfig $ethcard &> /dev/null && echo "No this interface..." && exit 3 read -p "Enter an alias: " ethAlias read -p "Enter IP: " ipAddr read -p "Mask: " netMask ifconfig $ethAlias $ipAddr netmask $netMask [ $debug -eq 1 ] && ifconfig $ethAlias

shell编程函数教程 bash脚本编程之函数 其实函数是很简单的,而他就是一种模块化编程的工具 函数:function,其实每一个函数就是一个功能组件,就是在主函数去调用要使用的函数 函数是可被调用:所以收函数有函数名 函数出现的地方,而自动被替换成函数定义的代码 函数定义 语法: FuncName() { 函数体 } function FuncName { 函数体 }

函数有两种返回值: 正常返回的数据: 函数中的打印语句,如echo或print 函数中命令的执行结果 执行状态返回值(所以函数是独立的个体,有自己的状态返回值): 取决于函数中执行的最后一条语句 如果想要自己指定:自定义:return N 函数遇到return会退出

函数是可以接受参数: 在函数体可以使用类似脚本调用位置参数一样的参数 $1, $2, ... 如下

$# $*, $@ 函数内部是可以嵌套函数的,如下这个实例 #!/bin/bash

function ShowUserInfo { [ $# -lt 1 ] && return 6 grep "^$1>" /etc/passwd | cut -d: -f3,7 }

function main { while true; do read -p "Plz enter a user name: " userName

if [ "$userName" == 'quit' ]; then echo "Quit" exit 0 fi

if ! id $userName &> /dev/null; then echo "No such user, please again." continue fi ShowUserInfo $userName done }

main

练习:写一个脚本,完成如下功能 1、显示如下菜单 disk) show disk info mem) show memory info cpu) show cpuinfo 2、显示用户选定的内容; #!/bin/bash

ShowMenu() { cat << EOF disk) show disk info mem) show memory info cpu) show cpuinfo EOF } main() { ShowMenu read -p "Plz choose an option: " option case $option in disk) df -h ;; mem) free -m ;; cpu) cat /proc/cpuinfo ;; *) echo "Wrong option" esac } main

介绍一个新的知识,函数中的变量 如果在函数中使用变量:变量作用域 在函数中使用了在主程序中声明的变量,重新赋值会直接修改主程序中的变量;

如果不期望函数与主程序中的变量冲突,函数中使用变量都用local修饰;即使用局部变量; 在函数中使用了在主程序中没有声明的变量,在函数执行结束后即被撤消,无论是否使用local修饰符;

上图,两个$1:一个是传递给函数的参数,一个是脚本的参数

如果想把脚本的全部位置参数,统统传递给脚本中某函数使用,怎么办? 使用$*传递

练习:写一个脚本,判定172.16.0.0网络内有哪些主机在线,在线的用绿色显示,不在线的用红色显示;要求,编程中使用函数;

C类网:ping NetAdd.HostAdd B类网:ping NetAadd.NetAadd.HostAdd.HostAdd #!/bin/bash CnetPing() { for i in {1..254}; do ping -c 1 -w 1 $1.$i }

CnetPing 192.168.10

BnetPing() { for j in {0.255}; do CnetPing $1.$j done } AnetPing() { for m in {0..255}; do BnetPing $1.$m done } netType=echo $1 | cut -d'.' -f1

if [ $netType -gt 0 -a $netType -le 126 ]; then AnetPing $1 elif [ $netType -ge 128 -a $netType -le 191 ]; then BnetPing $1 elif [ $netType -ge 192 -a $netType -le 223 ]; then CnetPing $1 else echo "Wrong" exit 3 fi

练习:写一个脚本,完成如下功能(使用函数): 1、提示用户输入一个可执行命令; 2、获取这个命令所依赖的所有库文件(使用ldd命令); 3、复制命令至/mnt/sysroot/对应的目录中 解释:假设,如果复制的是cat命令,其可执行程序的路径是/bin/cat,那么就要将/bin/cat复制到/mnt/sysroot/bin/目录中,如果复制的是useradd命令,而useradd的可执行文件路径为/usr/sbin/useradd,那么就要将其复制到/mnt/sysroot/usr/sbin/目录中; 4、复制各库文件至/mnt/sysroot/对应的目录中,其要求命令; [root@www myShell]# ldd /bin/cat linux-vdso.so.1 => (0x00007fffca3ff000) libc.so.6 => /lib64/libc.so.6 (0x00007f340955a000) /lib64/ld-linux-x86-64.so.2 (0x00007f34098f6000)

#!/bin/bash

target=/mnt/sysroot

clearCmd() { if which $cmd &> /dev/null; then cmdPath=which --skip-alias $cmd else echo "No such command" return 5 fi } cmdCopy() { cmdDir=dirname $1 [ -d ${target}${cmdDir} ] || mkdir -p ${target}${cmdDir} [ -f ${target}${1} ] || cp $1 ${target}${cmdDir} }

libCopy() { for lib in ldd $1 | grep -o "/[^[:space:]]\{1,\}"; do libDir=dirname $lib [ -d ${target}${libDir} ] || mkdir -p ${target}${libDir} [ -f ${target}${lib} ] || cp $lib ${target}${libDir} done } while true; do read -p "Enter a command: " cmd if [ "$cmd" == 'quit' ] ;then echo "quit" exit 0 fi clearCmd $cmd [ $? -eq 5 ] && continue

cmdCopy $cmdPath libCopy $cmdPath done

chroot命令

练习:写一个脚本,完成如下功能(使用函数): 1、脚本使用格式: mkscript.sh [-D|--description "script description"] [-A|--author "script author"] /path/to/somefile 2、如果文件事先不存在,则创建;且前几行内容如下所示: #!/bin/bash

Description: script description

Author: script author

3、如果事先存在,但不空,且第一行不是“#!/bin/bash”,则提示错误并退出;如果第一行是“#!/bin/bash”,则使用vim打开脚本;把光标直接定位至最后一行 4、打开脚本后关闭时判断脚本是否有语法错误 如果有,提示输入y继续编辑,输入n放弃并退出; 如果没有,则给此文件以执行权限; arguParse() { }

回顾: 进程管理: 信号:SIGHUP, SIGINT, SIGKILL, SIGTERM kill 常用杀死一个进程,默认使用的信号用数字表示 如kill 4450 killall杀死进程中的进程名称

nice, renice

top: M, T, P htop, epel源

base之case和函数:

case EXPRESSION in

pattern1) suite1 ;; pattern2) suite2 ;; 双分号就是跳出case语句的 ... patternN) suiteN ;; *) 匹配额外剩余的选项 other_suite ;; esac

函数: 模块化编程:最大化的代码重用,最小化的代码冗余;就是减小代码量 我们在利用函数一次的时候,使用函数也是比较好的 因为有个概念就是: 函数就是一个功能模块,以后我们想修改的话,就找到相应的模块即可了,而一个功能不做在函数体内,那对后期维护也是比较困难的 如果不使用模块化的函数的话后期就会比较麻烦

函数的接口: 调用: FuncName FuncName argu1 ... $1:

语法: FuncName() { body } function FuncName { body }

返回值: 执行结果:正常程序输出 状态结果:return N

脚本接受参数: 位置参数:$1, $2 脚本程序文件格式:

#!/bin/bash

Docs

变量声明:通常在这个位置 函数定义:函数通常在这个位置

主程序: ./script.sh argu1 argu2 FuncName $1 $2 $3 要知道函数的参数和脚本的函数是不一样的,不是一码事,但是使用方式相同,都是位置参数,按位置进行引用 如下实例 #!/bin/bash

sum() { echo $1 $2 } echo $1 $2 sum $1 $2 [root@www myShell]# bash hanshu.sh 3 4 3 4 3 4 虽然都是3 4,但是这两个数在内存空间不是占用同一个空间的变量

变量作用域:变量能够被使用的代码范围 变量被声明的位置决定了其作用域: 如果声明了,如果在函数内和函数外都有同一个位置变量名称,就是属于两个变量了,如果在函数中被声明了,作用域就是这个函数 如declare i A=20 写到了函数内,就是作用域只在函数内 因为在一个脚本中,有很多函数,而函数内还有函数,最内部的函数的查找次序为 查找次序: 内层函数 外层函数 主程序 bash解释器内置环境变量 因此在脚本中尽量少使用全局的变量(函数的全局变量会影响到整个脚本),函数中用local的定义变量

输出的值为 A=10 A=20 A=10(因为show()中,定义的declare -i A=20只影响函数本身)

#!/bin/bash declare -i UdID=500 haha() { declare -i A=30 buit }

buit() { echo $A } show() { declare -i A=20 echo $A } echo $UdID haha echo $UdID [root@www myShell]# ./shell1.sh
500 30 buit中echo $A,由于buit函数没有定义A的值,所以在其外部函数haha()中获取到了A的值,并等于30 500

思想:一个函数尽可能完成一个简单的功能,越单一越好,众多小函数,组合成大脚本或者是大程序即可 技术产生生产力 我们做的工作是支持类的工作,而创造生产力的是研发部门(这是不懂运维的人看到的结果) 不要钻牛角尖,不要为了技术本身而技术 把所学的知识变成自己的产品

没有执行力的人: 不要耍小聪明 不要追求太完美(追求完美反而是一种不完美) 过度思考的人(永远在想,没有执行)