Bash Shell脚本编程笔记总结(一)
2014-12-05 20:43
761 查看
本文是上课笔记总结,涉及细节知识点会在以后文章说明!
bash脚本编程:脚本程序:解释器解释执行;
shell: 交互式接口;编程环境;shell: 能够提供一些内部命令,并且能通过PATH环境变量找到外部命令;把命令提交给内核启动为进程;
编程环境:流程控制语句:顺序执行;循环执行;选择执行;
条件测试:真、假$?命令的状态结果:0: 真1-255: 假
过程式的编程语言的元素:变量、流程、函数、数组
变量:局部变量、本地变量、环境变量、位置参数变量、特殊变量变量:数值型:整型、浮点型、布尔型字符型:字符串
bash变量是弱类型;默认字符型;
变量引用:${VAR_NAME}引号:弱引用:""强引用:''命令引用:``
声明某变量为整型变量:
声明某变量为环境变量:
脚本的编写格式:第一行:写明解释器; #!/bin/bash注释行:所有以#开头的行均为注释行;会被解释器忽略;
执行脚本:赋予执行权限;指明路径执行;直接传递脚本给bash解释器
bash的选项:-n: 测试脚本中是否有语法 错误;-x: 调试执行;
算术运算:
流程控制之一:for循环将一段代码反复执行;表达式:
进入条件; 退出条件;循环体;STATEMENT1睡眠:sleep
循环次数:为列表中的元素的个数;LIST:列表,包含至少一个元素的字符串集合;(1) 直接给出;(2) 数值列表:(a) {start..end}例如:{1..10}(b) seq [start [step]] end
例如:添加9个用户,user101...user109;密码同用户名;
练习:于/tmp/test目录中创建10个空文件f1,.., f10;
练习:求100以内所有正整数之和;
练习:求100以内所有偶数之和;以及所有奇数之和;
练习:传递参数(文本文件路径)给脚本,统计所有文件的空白行数之和;显示此次共对多少文件进行统计;
练习:显示当前系统所有默认shell为bash的用户的总数;并统计此些用户ID之和;for id in `grep "/bin/bash$" /etc/passwd | cut -d: -f3`; do
for的几种特殊情况:(1) for省略,会自动获取脚本参数列表;(2) C编程风格:for ((变量赋值;循环条件;修正表达式)); doCMD1CMD2done
(3) 循环嵌套:for i in [LIST1]; doCMD1for j in [LIST2]; doCMD2...donedone
练习:写一个脚本(1) ping 172.16.X.Y内的所有主机;172.16.0-255.1-254
练习:写个脚本(1) 传递一些目录给脚本;(2) 逐个显示每个目录下的所有一级文件的内容类型;(3) 统计一个有多少个目录;一共显示了多少个文件;
bash命令退出和退出状态码命令在bash中执行结束退出时,其执行成功与否可通过退出状态码来记录
脚本的退出状态码取决于执行的最后一条命令;自定义退出状态码:exit #成功:0失败:1-255
bash 脚本编程之条件测试:if/then结构
条件测试:
测试表达式:(1) 整数测试;例如:1<2 (2) 字符串测试;例如:比较字符串相等与否(3) 文件测试;例如:判断文件类型
整数测试:A, B;需要双目操作符;A -gt B: 大于A -ge B: 大于和等于A -eq B: 等于A -lt B: 小于A -le B: 小于等于A -ne B: 不等于符合为真,否则为假注意:命令执行结果和状态返回结果不一样,需要判断。
字符串测试:A, BA > BA < BA >= BA <= BA == B或A = B:等值比较A != B: 不等于-z A: 判断A是否为空;空则为真,不空则假;-n A:判断A是否不空;不空则为值,空则为假;[ $A = $B ]"$A" =~ PATTERN如果变量A中给定的字符串能被模式匹配即为真,否则为假例如:过滤fdisk -l 结果的字符串
文件测试:$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) 存在,则显示有此文件;(2) 否则,则显示无此文件
练习:写一个脚本,传递一个文件路径参数给脚本(1) 如果脚本无参数,则显示必须给至少一个参数,退出脚本;退出码5;(2) 路径指向的文件如果不存在,则直接退出脚本;退出码为6;(3) 判断文件类型:(a) 如果是普通文件,则显示为"common file";(b) 如果是目录,则显示为"directory";(c) 如果是符号链接,则显示为"symbolic link file";(d) 如果是块设备,则显示为“block device file";(e) 如果是字符设备,则显示为"character device file";(f) 否则,则显示为“unkown”;
练习:写一个脚本,其使用格式如下所示(其中所有的script.sh均为用户给定的脚本名称,其要跟随脚本名称变化):script.sh {start|stop|restart|status}(1) 调用时至少传递一个参数;否则,则显示帮助信息,并退出脚本;(2) 如果参数为“start”, 则创建空文件/var/lock/subsys/script.sh,并显示“starting script.sh successfully.”;(3) 如果参数为“stop”,则删除空文件/var/lock/subsys/script.sh,并显示“stopping script.sh successfully.”;(4) 如果参数为“restart”,则删除空文件/var/lock/subsys/script.sh,并显示“stopping script.sh successfully.”;而后重新创建之,并显示“restarting script.sh successfully.”;(5) 如果参数为“status”,则(a) 如果文件/var/lock/subsys/script.sh文件存在,则显示“running”;(b) 否则,则显示为"stopped"(6) 其它任意参数,均显示帮助信息后退出;帮助信息格式为命令使用格式;
例如:求100以内所有偶数之和;遍历100以内所有正整数;
例如:传递一个参数给脚本,而后以此参数为用户名,添加此用户;#!/bin/bash if [ $# -ge 1 ];thenuseradd $1fi
if可以嵌套:
例如:传递一个参数给脚本,而后以此参数为用户名,不存在时,则添加此用户;条件取反:! CONDITION
练习:写一个脚本(1) 添加用户testuser1-tuser10;(2) 用户不存在时才添加;(3) 统计真正添加的用户数;
练习: 写一个脚本(1) 用ping命令测试172.16.100.X内的所有主机;(2) 将在线的主机输出出来;
双分支结构:
练习1: 写一个脚本(1) 用ping命令测试172.16.100.X内的所有主机;(2) 将所有主机的在线状态输出出来;
练习:写一个脚本(1) 传递两个整数参数给脚本;(2) 返回其较大一个;
练习:写一个脚本(1) 传递两个以上的整数给脚本;(2) 返回其较大者;
练习: 写一个脚本(1) 取得当前的主机名;(2) 如果当前的主机名为localhost,则将其修改为oracle;否则,显示其名称;
练习:写一个脚本(1) 传递一个参数给脚本,此参数为用户名;(2) 如果用户存在,则执行如下任务(a) 如果用户的id号小于500,显示其为管理员或系统用户;(b) 否则,显示其为普通用户;(3) 如果用户不存在,则添加之;
多分支的if语句:
练习:传递一个参数给脚本如果参数为quit,则显示说要退出脚本;如果参数为yes,则显示说继续;否则,则显示为无法识别;
练习:传递一个用户名参数给脚本(1) 如果用户的id号为0,则显示为管理员;(2) 如果用户的id号大于6000,则显示为guest; (3) 如果用户的id号大于500,则显示为普通用户;(4) 如果用户的id号大于0, 则显示为系统用户;(5) 否则,无法识别;
练习3:写一个脚本;(1) 传递一个磁盘设备文件给脚本;(2) 判断此设备是否存在;如果存在,则清除此设备上的所有分区;(3) 否则,则无此设备;
组合测试条件1:给条件添加逻辑操作符;或, -o: [ -z "$hostname" -o "$hostname" == 'localhost' ]与, -a: [ $uid -gt 0 -a $uid -lt 500 ]非:[ ! EXPRESSION ]
练习:写一个脚本,取得当前的主机名,判断(1) 如果主机名为空或为"localhost",则将其命名为stuX.oracle.com;(2) 否则,则显示主机名即可;
练习:写一个脚本,传递一个参数给脚本;此参数为用户名(1) 如果用户不存在,则直接退出脚本;(2) 如果用户存在,id=0,则显示为“system admin”0<id<500,则显示为“system user”id>=500,则显示为“Common user.”
练习:写一个脚本,传递一个参数给脚本;此参数为用户名(1) 如果用户不存在,则直接退出脚本;(2) 如果用户的id号大于等于500,且其shell为“以sh结尾的”类型,则显示“a user can log system.”;否则,显示用户无法登录;
组合测试条件2:短路操作符与:COMMAND1 && COMMAND2COMMAND1的退出状态如果为假,则COMMAND2不用运行,即可有最终结果;或:COMMAND1 || COMMAND2COMMAND1的退出状态如果为真,则COMMAND2不用运行,即可有最终结果;非:! COMMAND
练习:写一个脚本,完成如下任务:(1) 如果httpd进程或nginx进程处于运行中,则显示“web server started.”,并显示其监听的端口;(2) 否则显示“no web server.”;
交互式脚本:read [OPTIONS] [name ...]-p "PROMPT"-t #: 超时时长
给变量以默认值:[ -z "$VAR" ] && VAR=VALUE
练习:显示如下菜单给用户cpu) show cpu infomation;mem) show memory infomation;disk) show disk infomation;*) quit提示用户键入选项:(1) cpu: 显示CPU相关的信息(2) mem: 显示内存相关的信息(3) disk: 列出磁盘设备(4) 其它任何信息,即为退出脚本
语法格式:
PATTERN可使用通配符:*:任意长度的任意字符?: 任意单个字符[]:指定范围内的任意单个字符a|b: a或者b
练习:写一个脚本,使用tar工具把/etc目录备份至/backup目录中,名字为/backup/etc-日期时间.tar.{xz|bz2|gz};(1) 显示如下菜单xz) xz compress toolgzip) gzip compress toolbzip2) bzip2 compress tool*) wrong choice and quit(2) 根据用户选择的工具,执行相应操作;如果用户没有键入任何数据,则默认使用xz;
bash中生成伪随机数:$RANDOM0-32767
练习:生成10个随机数,返回其最大值;
练习:从所有同学中随机挑选一个学号;
while、until循环和函数:
练习:使用for循环,打印九九乘法表
while循环:while CONDITION; do循环体循环控制变量的修正表达式done
当CONDITION为“真”进入循环,直到“假”退出循环;
练习:计算100以内所有偶数之和;
until循环:until CONDITION; do循环体循环控制变量修正表达式done
当CONDITION为“假”时进入循环;为“真”退出循环;
练习:计算100以内所有偶数之和;
练习:分别使用while和until循环实现添加10个用户:myuser1-myuser10
练习:打印九九乘法表,要求内外层循环分别使用while或until;
循环控制命令:break:提前退出循环;break
: 退出N层循环;N省略时表示退出break语句所在的循环;continue: 提前结束本轮循环,而直接进入下轮循环;continue
:提前第N层的循环的本轮循环,而直接进入下轮循环;
while CONDITION; doCMD1if CONDITION2; thenbreak
fiCMD2...done
死循环:while true; do循环体done
until false; do循环体done
练习:写一个脚本,判断给定的用户是否登录了当前系统;(1) 如果登录了,则脚本终止;(2) 每5秒种,查看一次用户是否登录;
方法一:使用until循环
方法二:简化版
方法三:使用死循环
while的特殊用法:遍历指定文件的每一行while read line; do循环体done < /path/from/somefile
练习:找出其UID为偶数的所有用户的用户名;并显示其ID号;
练习:输出给定的文件的偶数行的行号,以及其行内信息统统改为大写字母输出;
练习:显示如下菜单给用户cpu) cpuifnomem) memory infomationdisk) disk infomationquit) quit
(1) 用户给定的选项后,显示相应的信息;而后提示用户再次选择;(2) 非正确选择也提示用户重新选择,并说明,如果想退出请输入"quit";
函数:function过程式编程:代码重用模块化编程;简洁
语法:function f_name {函数体}
f_name() {函数体}
调用:使用函数名函数名出现的地方,会被自动替换为函数代码;
练习:利用函数改写此前的服务脚本:
函数可接受参数:传递参数给函数:调用函数时,在函数名后给出参数列表即可;例如“testfunc arg1 arg2 arg3”
在函数体中可使用$1, $2,...来调用传递过来的各参数;可使用类似脚本的特殊变量:$*, $@: 一次性获取参数列表:$#: 参数的个数
判定用户输入用户名是否正确与否分别执行不同操作
练习:利用函数实现打印九九乘法表;
练习:写一个脚本,完成如下功能(使用函数): 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/对应的目录中;
变量的作用域:本地变量:整个脚本,在脚本的函数中也可调用,也可修改;局部变量:函数调用的整个生命周期;local VAR=VALUE
函数递归:函数直接或间接调用函数自身;1 1 2 3 5 8 13阶乘:10!n(n-1)!n(n-1)(n-2)!阶乘函数:fact() {if [ $1 -eq 0 -o $1 -eq 1 ]; thenecho 1else echo $[$1*$(fact $[$1-1])]fi}下一篇接:Bash Shell脚本编程笔记总结(二)
bash脚本编程:脚本程序:解释器解释执行;
shell: 交互式接口;编程环境;shell: 能够提供一些内部命令,并且能通过PATH环境变量找到外部命令;把命令提交给内核启动为进程;
编程环境:流程控制语句:顺序执行;循环执行;选择执行;
条件测试:真、假$?命令的状态结果:0: 真1-255: 假
过程式的编程语言的元素:变量、流程、函数、数组
变量:局部变量、本地变量、环境变量、位置参数变量、特殊变量变量:数值型:整型、浮点型、布尔型字符型:字符串
bash变量是弱类型;默认字符型;
变量引用:${VAR_NAME}引号:弱引用:""强引用:''命令引用:``
声明某变量为整型变量:
let VAR_NAME=VALUE declare -i VAR_NAME=VALUE
声明某变量为环境变量:
export VAR_NAME=VALUE declare -x VAR_NAME=VALUE
脚本的编写格式:第一行:写明解释器; #!/bin/bash注释行:所有以#开头的行均为注释行;会被解释器忽略;
执行脚本:赋予执行权限;指明路径执行;直接传递脚本给bash解释器
bash的选项:-n: 测试脚本中是否有语法 错误;-x: 调试执行;
算术运算:
$[EXPRESSION] let VAR_NAME=EXPRESSION $((EXPRESSION)) $(expr argu1 argu2 argu3)
流程控制之一:for循环将一段代码反复执行;表达式:
for VAR in LIST; do STATEMENT1 ... done
for VAR in LIST; do statement1; statement2; ...; done
for ((初始化循环控制变量;循环条件;修正表达式)); do 循环体 done
进入条件; 退出条件;循环体;STATEMENT1睡眠:sleep
循环次数:为列表中的元素的个数;LIST:列表,包含至少一个元素的字符串集合;(1) 直接给出;(2) 数值列表:(a) {start..end}例如:{1..10}(b) seq [start [step]] end
seq LAST seq FIRST LAST seq FIRST STEP LAST(3) 返回列表的命令;(4) globbing;(5) 变量引用;$*, $@例如:添加3个用户,user1, user2, user3; 密码同用户名;
#vi useradd.sh #!/bin/bash for username in user1 user2 user3; do useradd $username echo $username |passwd --stdin $username done
例如:添加9个用户,user101...user109;密码同用户名;
#vi useradd2.sh #!/bin/bash for i in {1..9}; do useradd user10$i echo user10$i |passwd --stdin user10$i done
练习:于/tmp/test目录中创建10个空文件f1,.., f10;
#vi addfile.sh #!/bin/bash directory=/tmp/test-$(date '+%Y%m%h') mkdir $directory for i in {1..10} ; do touch $directory/f$i done练习:写一个脚本,(1) 创建"/tmp/test-当前时间"目录;(2) 添加10用户tuser1,.., tuser10; (3) 在"/tmp/test-当前时间"目录中,创建10空文件f1,...,f10;(4) 修改f1的属主为tuser1;依次类推;
#vi creatdir.sh #!/bin/bash dir=/tmp/test-$(date '+%F-%H-%S') mkdir $dir for i in {1..10} ; do useradd testuser$i touch $dir/f$i chown testuser$i $dir/f$i done rm -rf $dir userdel testuser$i
练习:求100以内所有正整数之和;
#vi plus.sh #!/bin/bash sum=0 for i in `seq 100`;do sum=$(($sum+$i)) done echo $sum
练习:求100以内所有偶数之和;以及所有奇数之和;
#!/bin/bash declare -i sum=0 for i in `seq 1 2 100`;do sum=$(($sum+$i)) done echo $sum declare -i sum1=0 for i in `seq 2 2 100`; do sum1=$(($sum1+$i)) done echo $sum1 练习5:计算当前系统上所有用户ID之和; #!/bin/bash sum=0 for i in `cut -d: -f3 /etc/passwd` ; do sum=$(($sum+$i)) done echo $sum
练习:传递参数(文本文件路径)给脚本,统计所有文件的空白行数之和;显示此次共对多少文件进行统计;
#!/bin/bash sum=0 num=0 echo $# for i in `seq 1 $# `; do num=`grep '^$' $1 |wc -l` sum=$(($num+$sum)) shift done echo $sum
练习:显示当前系统所有默认shell为bash的用户的总数;并统计此些用户ID之和;for id in `grep "/bin/bash$" /etc/passwd | cut -d: -f3`; do
#!/bin/bash sum=0 num=0 for i in `grep 'bash$' /etc/passwd |cut -d: -f3` ; do sum=$(($sum+$i)) num=$(($num+1)) done
for的几种特殊情况:(1) for省略,会自动获取脚本参数列表;(2) C编程风格:for ((变量赋值;循环条件;修正表达式)); doCMD1CMD2done
(3) 循环嵌套:for i in [LIST1]; doCMD1for j in [LIST2]; doCMD2...donedone
练习:写一个脚本(1) ping 172.16.X.Y内的所有主机;172.16.0-255.1-254
for i in {0..255}; do for j in {1..254}; do ping -c1 -w1 172.16.$i.$j done done
练习:写个脚本(1) 传递一些目录给脚本;(2) 逐个显示每个目录下的所有一级文件的内容类型;(3) 统计一个有多少个目录;一共显示了多少个文件;
#!/bin/bash # declare -i dirs=0 declare -i files=0 for d in $*; do for f in $d/*; do file $f let files++ done let dirs++ done echo "Directories: $dirs." echo "Files: $files."
bash命令退出和退出状态码命令在bash中执行结束退出时,其执行成功与否可通过退出状态码来记录
#echo $?
脚本的退出状态码取决于执行的最后一条命令;自定义退出状态码:exit #成功:0失败:1-255
#!/bin/bash ls /varr retval=$? echo 'hello' exit $retvel以ls /varr的结果状态为退出状态注意:提前退出脚本,也可使用exit命令实现
bash 脚本编程之条件测试:if/then结构
条件测试:
test EXPRESSION [ EXPRESSION ] [[ EXPRESSION ]]COMMAND:使用的是命令退出状态码1.使用命令执行结果a.使用命令引用b.使用比较操作符例如:
[ `id -u $username ` -lt 500 ] userid=`id -u $username` [ $userid -lt 500]2.使用命令的退出状态码a.先运行命令b.引用状态码引用方式两种:1.if COMMAND &>/dev/null;then引用的是命令运行的退出状态码注意:COMMAND不能被使用命令引用,COMMAND的执行结果通常没有意义,所以其结果通常(&>)被定向至/dev/null2.先执行命令,后判断退出状态码是否为0COMMAND
retval=$? if [ $? -eq 0 ] ; then
测试表达式:(1) 整数测试;例如:1<2 (2) 字符串测试;例如:比较字符串相等与否(3) 文件测试;例如:判断文件类型
整数测试:A, B;需要双目操作符;A -gt B: 大于A -ge B: 大于和等于A -eq B: 等于A -lt B: 小于A -le B: 小于等于A -ne B: 不等于符合为真,否则为假注意:命令执行结果和状态返回结果不一样,需要判断。
字符串测试:A, BA > BA < BA >= BA <= BA == B或A = B:等值比较A != B: 不等于-z A: 判断A是否为空;空则为真,不空则假;-n A:判断A是否不空;不空则为值,空则为假;[ $A = $B ]"$A" =~ PATTERN如果变量A中给定的字符串能被模式匹配即为真,否则为假例如:过滤fdisk -l 结果的字符串
#disk=/dev/sda #[[ $dsk=~/dev/sd[a-z] ]] 变量和模式匹配 #echo $? 0 为真 #disk=/dev/xda #[[ $disk =~ /dev/sd[a-z] ]] 变量和模式不匹配 #echo $? 1 为假
文件测试:$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) 存在,则显示有此文件;(2) 否则,则显示无此文件
#!/bin/bash if [ -e $1 ] ; then echo "$1 exist" else echo "$1 is not exist" fi
练习:写一个脚本,传递一个文件路径参数给脚本(1) 如果脚本无参数,则显示必须给至少一个参数,退出脚本;退出码5;(2) 路径指向的文件如果不存在,则直接退出脚本;退出码为6;(3) 判断文件类型:(a) 如果是普通文件,则显示为"common file";(b) 如果是目录,则显示为"directory";(c) 如果是符号链接,则显示为"symbolic link file";(d) 如果是块设备,则显示为“block device file";(e) 如果是字符设备,则显示为"character device file";(f) 否则,则显示为“unkown”;
#!/bin/bash if [ $# -lt 1 ] ; then echo "you mast input a number" exit 5 if [ ! -e $1 ] ; then echo "the file is not exist" exit 6 if [ -f $1 ] ; then echo "$1 is commonfile" elif [ -d $1 ] ; then echo "$1 is directory" elif [ -L $1 ] ; then echo "$1 is symbolic link file" elif [ -b $1 ] ; then echo "$1 is block device file" elif [ -c $1 ] ; then echo "$1 is character device file" else echo "$1 is unknow" fi
练习:写一个脚本,其使用格式如下所示(其中所有的script.sh均为用户给定的脚本名称,其要跟随脚本名称变化):script.sh {start|stop|restart|status}(1) 调用时至少传递一个参数;否则,则显示帮助信息,并退出脚本;(2) 如果参数为“start”, 则创建空文件/var/lock/subsys/script.sh,并显示“starting script.sh successfully.”;(3) 如果参数为“stop”,则删除空文件/var/lock/subsys/script.sh,并显示“stopping script.sh successfully.”;(4) 如果参数为“restart”,则删除空文件/var/lock/subsys/script.sh,并显示“stopping script.sh successfully.”;而后重新创建之,并显示“restarting script.sh successfully.”;(5) 如果参数为“status”,则(a) 如果文件/var/lock/subsys/script.sh文件存在,则显示“running”;(b) 否则,则显示为"stopped"(6) 其它任意参数,均显示帮助信息后退出;帮助信息格式为命令使用格式;
#!/bin/bash # # chkconfig: 2345 95 5 # description: test service script # prog=`basename $0` lockfile=/var/lock/subsys/$prog if [ $# -lt 1 ]; then echo "Usage: $prog {start|stop|restart|status}" exit 1 fi if [ "$1" == 'start' ]; then if [ -e $lockfile ]; then echo "$prog is aleady running." exit 1 else touch $lockfile echo "Starting $prog succefully." fi elif [ "$1" == 'stop' ]; then if [ -e $lockfile ]; then rm -f $lockfile echo "Stopping $prog succefully." else echo "$prog is not running." exit 1 fi elif [ "$1" == 'restart' ]; then if [ -e $lockfile ]; then rm -f $lockfile echo "Stopping $prog succefully." touch $lockfile echo "Starting $prog succefully." else touch $lockfile echo "Starting $prog succefully." fi elif [ "$1" == 'status' ];then if [ -e $lockfile ];then echo "$prog is running." else echo "$prog is stopped." fi else echo "Usage: $prog {start|restart|status|stop}" exit 1 fi条件测试语法:单分支:
if CONDITION; then CMD1 CMD2 ... fi
例如:求100以内所有偶数之和;遍历100以内所有正整数;
#!/bin/bash declare i sum=0 for i in {1..100} ; do if [[ $[$i%2] -eq 0 ]] ; then sum=$(($sum+$i)) fi done echo $sum
例如:传递一个参数给脚本,而后以此参数为用户名,添加此用户;#!/bin/bash if [ $# -ge 1 ];thenuseradd $1fi
if可以嵌套:
if CONDITION1; then if CONDITION2; then CMD fi fi
例如:传递一个参数给脚本,而后以此参数为用户名,不存在时,则添加此用户;条件取反:! CONDITION
#!/bin/bash 至少有个参数 if [ $# -ge 1 ];then if ! id $1 &>/dev/null;then 条件取反 useradd $1 fi fiif条件判断为真才能执行then下面的步骤id $1 的状态如果是0,表示执行状态成功了,取反后条件是假,then就不执行id $1 的执行状态如果是1,表示执行状态是失败的,取反后条件判断是真,then就开始执行
练习:写一个脚本(1) 添加用户testuser1-tuser10;(2) 用户不存在时才添加;(3) 统计真正添加的用户数;
#!/bin/bash # declare -i newusers=0 for i in {1..10}; do if ! id testuser$i &> /dev/null; then useradd testuser$i let newusers++ fi done echo "New users: $newusers."
练习: 写一个脚本(1) 用ping命令测试172.16.100.X内的所有主机;(2) 将在线的主机输出出来;
#!/bin/bash # for i in {1..254}; do if ping -c1 -w1 172.16.100.$i &> /dev/null; then echo "172.16.100.$i is up." fi done
双分支结构:
if CONDITION-TRUE; then 分支1 else 分支2 fi
练习1: 写一个脚本(1) 用ping命令测试172.16.100.X内的所有主机;(2) 将所有主机的在线状态输出出来;
#!/bin/bash for i in {1..254}do if ping -c1 -w1 172.16.100.$i &>/dev/null;then echo "172.16.100.$i is up" else echo "172.16.100.$i is down" fi done
练习:写一个脚本(1) 传递两个整数参数给脚本;(2) 返回其较大一个;
#!/bin/bash declare -i max=0 for i in $* ; do if [ $i -gt $max ] ; then max=$i fi done echo $max
练习:写一个脚本(1) 传递两个以上的整数给脚本;(2) 返回其较大者;
#!/bin/bash # declare -i max=0 for i in $*; do if [ $i -gt $max ]; then max=$i fi done echo "max: $max."
练习: 写一个脚本(1) 取得当前的主机名;(2) 如果当前的主机名为localhost,则将其修改为oracle;否则,显示其名称;
#!/bin/bash hostname=`hostname` if [ "$hostname" == 'localhost' ]; then hostname oracle echo "oracle" > /proc/sys/kernel/hostname fi练习:写一个脚本(1) 传递两个文本文件路径给脚本;(2) 显示两个文件中空白行数较多的文件及其空白行数;(3) 显示两个文件中总行数较多的文件及其行数;
#!/bin/bash if [ $# -ge 2 ] ; then if [[ $(wc -l $1|cut -d" " -f1 ) -gt $(wc -l $2| cut -d" " -f1 ) ]] ; then if [[ $(grep "^$" $1 | wc -l) -gt $(grep "^$" $2 | wc -l) ]] ; then echo "the more space line file is $1 " echo "the file space line is $(grep "^$" $1 | wc -l)" else echo "the more space line file is $2 " echo "the file space line is $(grep "^$" $2 | wc -l)" fi echo "the more line file is $1 " echo "the line have $(wc -l $1)" else echo "the more line file is $2 " echo "the line have $(wc -l $2)" fi fi
练习:写一个脚本(1) 传递一个参数给脚本,此参数为用户名;(2) 如果用户存在,则执行如下任务(a) 如果用户的id号小于500,显示其为管理员或系统用户;(b) 否则,显示其为普通用户;(3) 如果用户不存在,则添加之;
#!/bin/bash # if id $1 &> /dev/null; then userid=`id -u $1` if [ $userid -lt 500 ]; then echo "$1 is sysadmin or sysuser." else echo "A common user." fi else useradd $1 if [ $? -eq 0 ];then echo "Add user $1." else echo "Cannot add $1." fi fi
多分支的if语句:
if CONDITION1-TRUE; then 分支1 elif CONDITION2-TRUE; then 分支2 elif CONDITION3-TRUE; then 分支3 ... else 分支n fi
练习:传递一个参数给脚本如果参数为quit,则显示说要退出脚本;如果参数为yes,则显示说继续;否则,则显示为无法识别;
#!/bin/bash if [[ "$1" == 'quit' ]] ; then echo "quit" elif [[ "$1" == 'yes' ]] ; then echo "yes" else echo "unkown" fi
练习:传递一个用户名参数给脚本(1) 如果用户的id号为0,则显示为管理员;(2) 如果用户的id号大于6000,则显示为guest; (3) 如果用户的id号大于500,则显示为普通用户;(4) 如果用户的id号大于0, 则显示为系统用户;(5) 否则,无法识别;
#!/bin/bash if id $1 &/dev/null ; then userid=`id -u $1` if [ $userid -eq 0 ] ; then echo "$1 is sysadmin" elif [ $userid -gt 60000 ] ; then echo "$1 is a guest user" elif [ $userid -gt 500 ] ; then echo "$1 is a common user" elif [ $userid -gt 0 ] ; then echo "$1 is a system user" else echo "$1 is unknown " fi
练习3:写一个脚本;(1) 传递一个磁盘设备文件给脚本;(2) 判断此设备是否存在;如果存在,则清除此设备上的所有分区;(3) 否则,则无此设备;
#!/bin/bash if fdisk -l $1 ; then dd if=/dev/zero of=$1 bs=512 count=1 else echo "no such device" fi
组合测试条件1:给条件添加逻辑操作符;或, -o: [ -z "$hostname" -o "$hostname" == 'localhost' ]与, -a: [ $uid -gt 0 -a $uid -lt 500 ]非:[ ! EXPRESSION ]
练习:写一个脚本,取得当前的主机名,判断(1) 如果主机名为空或为"localhost",则将其命名为stuX.oracle.com;(2) 否则,则显示主机名即可;
#!/bin/bash # hostname=`hostname` if [ "$hostname" == 'localhost' -o -z "$hostname" ]; then hostname stu100.oracle.com #echo "stu100.oracle.com" > /proc/sys/kernel/hostname else echo "The hostname is: $hostname." fi
练习:写一个脚本,传递一个参数给脚本;此参数为用户名(1) 如果用户不存在,则直接退出脚本;(2) 如果用户存在,id=0,则显示为“system admin”0<id<500,则显示为“system user”id>=500,则显示为“Common user.”
#!/bin/bash # if ! id $1 &> /dev/null; then echo "No such user." exit 1 fi uid=$(id -u $1) if [ $uid -eq 0 ]; then echo "Sys Admin." elif [ $uid -gt 0 -a $uid -lt 500 ];then echo "Sys User." else echo "Common User." fi
练习:写一个脚本,传递一个参数给脚本;此参数为用户名(1) 如果用户不存在,则直接退出脚本;(2) 如果用户的id号大于等于500,且其shell为“以sh结尾的”类型,则显示“a user can log system.”;否则,显示用户无法登录;
#!/bin/bash # if ! id $1 &> /dev/null; then echo "No such user." exit 1 fi if [[ `id -u $1` -ge 500 ]] && [[ `grep "^$1\>" /etc/passwd | cut -d: -f7` =~ /bin/.*sh$ ]]; then echo "a user can log system." else echo "a user cannot log." fi
组合测试条件2:短路操作符与:COMMAND1 && COMMAND2COMMAND1的退出状态如果为假,则COMMAND2不用运行,即可有最终结果;或:COMMAND1 || COMMAND2COMMAND1的退出状态如果为真,则COMMAND2不用运行,即可有最终结果;非:! COMMAND
[ ! -d /tmp/test ] && mkdir /tmp/test^C [ -d /tmp/test ] || mkdir /tmp/test
练习:写一个脚本,完成如下任务:(1) 如果httpd进程或nginx进程处于运行中,则显示“web server started.”,并显示其监听的端口;(2) 否则显示“no web server.”;
if pidof httpd &> /dev/null && pidof nginx &> /dev/null;
交互式脚本:read [OPTIONS] [name ...]-p "PROMPT"-t #: 超时时长
给变量以默认值:[ -z "$VAR" ] && VAR=VALUE
练习:显示如下菜单给用户cpu) show cpu infomation;mem) show memory infomation;disk) show disk infomation;*) quit提示用户键入选项:(1) cpu: 显示CPU相关的信息(2) mem: 显示内存相关的信息(3) disk: 列出磁盘设备(4) 其它任何信息,即为退出脚本
#!/bin/bash # cat << EOF cpu) show cpu infomation; mem) show memory infomation; disk) show disk infomation; *) quit ========================================================= EOF read -p "Your choice: " choice if [[ "$choice" == 'cpu' ]]; then lscpu elif [[ "$choice" == 'mem' ]]; then free -m elif [[ "$choice" == 'disk' ]]; then fdisk -l /dev/sd[a-z] else echo "quit" exit 0 ficase语句:简洁版的多分支if语句;
语法格式:
case 变量引用 in PATTERN1) 分支1 ;; PATTERN2) 分支2 ;; ... *) 分支n ;; esac
PATTERN可使用通配符:*:任意长度的任意字符?: 任意单个字符[]:指定范围内的任意单个字符a|b: a或者b
练习:写一个脚本,使用tar工具把/etc目录备份至/backup目录中,名字为/backup/etc-日期时间.tar.{xz|bz2|gz};(1) 显示如下菜单xz) xz compress toolgzip) gzip compress toolbzip2) bzip2 compress tool*) wrong choice and quit(2) 根据用户选择的工具,执行相应操作;如果用户没有键入任何数据,则默认使用xz;
#!/bin/bash cat<<EOF xz)xz compress tool gzip)gzip compress tool bzip2)bzip2 compress tool *)wrong choice and quit ======================================================= EOF read -t 5 -p "pls input the choice:" command [ -z $command ] && command="xz" ! [ -d /backup ] || mkdir /backup file_path=/backup/etc"-$(date +%Y%m%d%H%M).tar" echo $file_path case $command in "xz") echo "xz" #tar -Jcf $file_path.xz /etc/* ;; "gzip") echo "gzip" #tar -zcf $file_path.gz /etc/* ;; "bzip2") echo "bzip2" #tar -jcf $file_path.bzip2 /etc/* ;; *) echo "wrong choice" ;; esac练习:使用case语句编写一个服务脚本框架
#!/bin/bash # # chkconfig: # description: # prog=`basename $0` lockfile=/var/lock/subsys/$prog case $1 in "start") if [ -e $lockfile ];then echo "$prog is aleady running" exit 1 else touch $lockfile echo "Starting $prog succefully" fi ;; "stop") if [ -e $lockfile ];then rm -f $lockfile echo "Stopping $prog succefully" else echo "$porg is not running" exit 1 fi ;; "restart") if [ -e $lockfile ];then rm -f $lockfile echo "Stopping $prog succefully" touch $lockfile echo "Starting $prog succefully" else touch $lockfile echo "Starting $prog succefully" fi ;; "status") if [ -e $lockfile ];then echo "$prog is running" else echo "$prog is stopped" fi ;; *) echo "Usage: $prog {start|restart|status|stop}" exit 1 ;; esac
bash中生成伪随机数:$RANDOM0-32767
练习:生成10个随机数,返回其最大值;
#!/bin/bash declare -i max=0 for i in {1..10};do random=$RANDOM LIST="$LIST $random" if [ $random -ge $max ];then max=$random fi done echo "List Number:$LIST" echo "Max Number:$max"
练习:从所有同学中随机挑选一个学号;
#!/bin/bash i=$[$RANDOM%36+1] j=$[$RANDOM%36+1] while [ $i -ne $j ] ; do echo $i echo $j break done # echo $[`date +%N`%36+1] # echo $[`head /dev/urandom | cksum |cut -d" " -f1`%36+1]
while、until循环和函数:
练习:使用for循环,打印九九乘法表
#!/bin/bash # for i in {1..9}; do for j in `seq 1 $i`; do echo -n -e "${j}x${i}=$[$i*$j]\t" done echo done
while循环:while CONDITION; do循环体循环控制变量的修正表达式done
当CONDITION为“真”进入循环,直到“假”退出循环;
练习:计算100以内所有偶数之和;
#!/bin/bash # declare -i i=1 declare -i evensum=0 while [ $i -le 100 ]; do if [ $[$i%2] -eq 0 ]; then let evensum+=$i fi let i++ done echo $evensum
until循环:until CONDITION; do循环体循环控制变量修正表达式done
当CONDITION为“假”时进入循环;为“真”退出循环;
练习:计算100以内所有偶数之和;
#!/bin/bash # declare i=1 declare evensum=0 until [ $i -gt 100 ]; do if [ $[$i%2] -eq 0 ]; then let evensum+=$i fi let i++ done echo $evensum
练习:分别使用while和until循环实现添加10个用户:myuser1-myuser10
#!/bin/bash # declare -i i=1 declare -i j=0 until [ $i -gt 10 ]; do if ! id myuser$i &> /dev/null; then useradd myuser$i let j++ fi let i++ done echo "Add users [total]: $j."
练习:打印九九乘法表,要求内外层循环分别使用while或until;
#!/bin/bash declare i=1 declare j=1 while [ $i -le 9 ] ; do while [ $j -le 9 ] ; do if [ $j -gt $i ] ; then break fi echo -e -n "${j}x${i}=$[$i*$j]\t" let j++ done echo let j=1 let i++ done
循环控制命令:break:提前退出循环;break
: 退出N层循环;N省略时表示退出break语句所在的循环;continue: 提前结束本轮循环,而直接进入下轮循环;continue
:提前第N层的循环的本轮循环,而直接进入下轮循环;
while CONDITION; doCMD1if CONDITION2; thenbreak
fiCMD2...done
死循环:while true; do循环体done
until false; do循环体done
练习:写一个脚本,判断给定的用户是否登录了当前系统;(1) 如果登录了,则脚本终止;(2) 每5秒种,查看一次用户是否登录;
方法一:使用until循环
#!/bin/bash # declare -i status=0 who | grep "centos" &> /dev/null status=$? until [ $status -eq 0 ]; do sleep 5 who | grep "centos" &> /dev/null status=$? done echo "centos is logged on."
方法二:简化版
#!/bin/bash # declare -i status=0 until who | grep "centos" &> /dev/null; do sleep 5 done echo "centos is logged on."
方法三:使用死循环
#!/bin/bash # while true; do who | grep "centos" &> /dev/null if [ $? -eq 0 ];then break fi sleep 5 done echo "centos is logged."
while的特殊用法:遍历指定文件的每一行while read line; do循环体done < /path/from/somefile
练习:找出其UID为偶数的所有用户的用户名;并显示其ID号;
#!/bin/bash # file=/etc/passwd while read line; do userid=`echo $line | cut -d: -f3` if [ $[$userid%2] -eq 0 ]; then echo $line | cut -d: -f1,3 fi done < $file
练习:输出给定的文件的偶数行的行号,以及其行内信息统统改为大写字母输出;
#!/bin/bash declare -i i=1 while read line; do if [ $[$i%2] -eq 0 ]; then echo -n "$i " echo $line | tr 'a-z' 'A-Z' fi let i++ done < /path/from/somefile
练习:显示如下菜单给用户cpu) cpuifnomem) memory infomationdisk) disk infomationquit) quit
(1) 用户给定的选项后,显示相应的信息;而后提示用户再次选择;(2) 非正确选择也提示用户重新选择,并说明,如果想退出请输入"quit";
#!/bin/bash # while true; do cat << EOF cpu) cpu mem) memory disk) disk quit) quit EOF read -p "Your choice: " choice case $choice in cpu) lscpu ;; mem) free ;; disk) fdisk -l /dev/sd[a-z] ;; quit) break ;; esac done
函数:function过程式编程:代码重用模块化编程;简洁
语法:function f_name {函数体}
f_name() {函数体}
调用:使用函数名函数名出现的地方,会被自动替换为函数代码;
练习:利用函数改写此前的服务脚本:
#!/bin/bash # prog=`basename $0` lockfile=/var/lock/subsys/$prog start() { if [ -e $lockfile ]; then echo "$prog is already running." else touch $lockfile [ $? -eq 0 ] && echo "Starting $prog finished." fi } stop() { if [ -e $lockfile ]; then rm -f $lockfile [ $? -eq 0 ] && echo "Stoping $prog finished." else echo "$prog is stopped yet." fi } case $1 in "start") start ;; "stop") stop ;; "restart") stop start ;; *) echo "Usage: $prog {start|stop|restart}" exit 1 esac函数的返回值:函数的执行结果返回值:函数中使用打印语句:echo, printf函数体中OS命令执行结果的输出函数的退出状态码:默认取决于函数体执行的最后一个命令的退出状态码;自定义退出状态码:return [0-255]注意:函数体运行时,一旦遇到return语句,函数即返回;
函数可接受参数:传递参数给函数:调用函数时,在函数名后给出参数列表即可;例如“testfunc arg1 arg2 arg3”
在函数体中可使用$1, $2,...来调用传递过来的各参数;可使用类似脚本的特殊变量:$*, $@: 一次性获取参数列表:$#: 参数的个数
判定用户输入用户名是否正确与否分别执行不同操作
#!/bin/bash # showuserinfo() { [ $# -lt 1 ] && return 1 ! id $1 &> /dev/null && return 2 grep "^$1\>" /etc/passwd | cut -d: -f7 [ $? -eq 0 ] && return 0 || return 3 } while true; do read -p "Enter a username: " username [ "$username" == 'quit' ] && break showuserinfo $username [ $? -ne 0 ] && echo "There is something wrong." done
练习:利用函数实现打印九九乘法表;
#!/bin/bash func() { declare i=1 declare j=1 while [ $i -le 9 ] ; do while [ $j -le 9 ] ; do if [ $j -gt $i ] ; then break fi echo -e -n "{$j}x$i=$[$i*$j]\t" let j++ done echo let j=1 let i++ done } func
练习:写一个脚本,完成如下功能(使用函数): 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/对应的目录中;
#!/bin/bash # target=/mnt/sysroot [ -d $target ] || mkdir $target preCmd() { if which $1 &> /dev/null; then cmdpath=$(which --skip-alias $1) return 0 else echo "No such command." return 1 fi } cmdCopy() { cmddir=$(dirname $cmdpath) [ -d $target/$cmddir ] || mkdir -p $target/$cmddir [ -f $target/$cmdpath ] || cp $1 $target/$cmddir return 0 } libCopy() { for lib in $(ldd $1 | grep -E -o "/[^[:space:]]+"); do libdir=$(dirname $lib) [ -d $target/$libdir ] || mkdir -p $target/$libdir [ -f $target/$lib ] || cp $lib $target/$libdir done return 0 } main() { while true; do read -p "Plz enter a command(quit for quiting): " cmd [ "$cmd" == 'quit' ] && break preCmd $cmd if [ $? -eq 0 ]; then cmdCopy $cmdpath libCopy $cmdpath fi done } main
变量的作用域:本地变量:整个脚本,在脚本的函数中也可调用,也可修改;局部变量:函数调用的整个生命周期;local VAR=VALUE
函数递归:函数直接或间接调用函数自身;1 1 2 3 5 8 13阶乘:10!n(n-1)!n(n-1)(n-2)!阶乘函数:fact() {if [ $1 -eq 0 -o $1 -eq 1 ]; thenecho 1else echo $[$1*$(fact $[$1-1])]fi}下一篇接:Bash Shell脚本编程笔记总结(二)
相关文章推荐
- 顶嵌学员学习笔记:Shell脚本编程总结
- shell脚本编程部分技巧总结
- BASH命令和SHELL脚本总结(3)命令篇
- BASH命令和SHELL脚本总结(12)如何对字段进行操作
- 脚本文件(shell perl python)编程的总结的框架。
- BASH命令和SHELL脚本总结(18)使用ACK在指定类型的文件中进行查找
- Unix整理笔记——基本shell脚本编程——里程碑M14
- BASH命令和SHELL脚本总结(11)比较两个文件创建时间的先后
- shell脚本编程总结
- Shell脚本编程的知识点总结
- bash shell笔记1 脚本基础知识
- 总结三种Shell脚本编程中避免SFTP输入密码的方法
- BASH命令和SHELL脚本总结(9)其它篇
- BASH命令和SHELL脚本总结(5)赋值篇
- BASH命令和SHELL脚本总结(14)cat和grep和sort
- Shell编程之脚本参数传递笔记
- BASH命令和SHELL脚本总结(2)常识篇
- BASH命令和SHELL脚本总结(7)判断篇
- BASH命令和SHELL脚本总结(8)循环篇
- bash shell笔记6 脚本控制