回顾:
字符串处理
数组
bash交互
if
for
case分支选择结构:
case 词 in [模式 [| 模式]...) 命令 ;;]... esac
case 变量引用 in
模式1)
分支1
;;
模式2)
分支2
;;
...
*)
默认分支
;;
esac
模式(PATTERN):
1.普通的文本字符
2.globbing风格的通配符:
*:任意长度任意字符
?:任意的单个字符
[]:范围内的任意单个字符
[^]:范围外的任意单个字符
3.|:或
写一个脚本:
提示用户输入信息,然后判断用户输入的信息是否合法;
#!/bin/bash
#
read -p "Please make your choice[yes of no]: " CHOICE
case $CHOICE in
yes)
echo "right."
;;
no)
echo "wrong."
;;
*)
echo "Unknown."
;;
esac
if CONDITION1 ; then
STATEMENT
elif CONDITION2 ; then
STATEMENT
elif CONDITION3 ; then
STATEMENT
...
else
STATEMENT
fi
if的多分支结构和case的分支结构之间的区别:
相同点:
1.都是条件为真,执行对应分支的语句;条件为假,就不执行;
2.都可以设置默认分支语句,即:所有条件都不匹配的时候,所执行的语句;
不同点:
1.if是根据命令的执行状态返回值来判断正确与否;case是根据变量的值的取值内容是否匹配模式来判断正确与否;
2.case的每个分支都必须使用';;'结束;
管理用户账户的脚本,第四版:利用case语句实现
#!/bin/bash
#
if [ $# -lt 2 ] ; then
echo "Usage: $(basename $0) -a User1,User2,...,UserN | -d User1,User2,...,UserN."
exit 5
fi
case $1 in
-a)
for I in $(echo $2 | tr ',' ' ') ; do
if id $I &> /dev/null ; then
echo "$I exists already."
else
useradd $I
echo $I | passwd --stdin $I &> /dev/null
echo "Create $I successfully."
fi
done
;;
-d)
for J in $(echo $2 | tr ',' ' ') ; do
if id $J &> /dev/null ; then
userdel -r $J
echo "Delte $J finished."
else
echo "User $J does not exist."
fi
done
;;
*)
echo "Usage: $(basename $0) -a User1,User2,...,UserN | -d User1,User2,...,UserN."
exit 6
esac
while
while 命令; do 命令; done
while CONDITION ; do
循环体
done
进入循环条件:CONDITION一直为真;
退出循环条件:CONDITION为假;
until
until 命令; do 命令; done
until CONDITION ; do
循环体
done
进入循环条件:CONDITION一直为假;
退出循环条件:CONDITION为真;
while CONDITION ; do CMD ; done
相当于
until ! CONDITION ; do CMD ; done
注意:对于while和until两个循环结构来讲,如果要实施变量增量操作,必须手动给出;
利用while和until循环结构,计算100以内所有整数的和;
#!/bin/bash
#
declare -i I=1
while [ $I -le 100 ] ; do
let SUM+=$I
let I++
done
echo $SUM
#!/bin/bash
#
declare -i I=1
until [ $I -gt 100 ] ; do
let SUM+=$I
let I++
done
echo $SUM
循环控制语句:
continue
break
continue:
continue [n]
提前结束第n层的本次循环,直接进入下一轮条件判断,若符合循环进入条件,则开启下一轮循环;
break:
break [n]
提前技术第n层循环;不再继续后续循环;
无限循环用法:
while true ; do
循环体
done
until false ; do
循环体
done
在此类的循环结构中,必须适当的使用continue和break,以保证循环不会一直持续下去;
能够实现遍历功能的while循环和until循环;
while read LINES ; do
循环体
done < /PATH/FROM/SOMEFILE
until ! read LINES ; do
循环体
done < /PATH/FROM/SOMEFILE
select
select循环主要用于创建一个菜单式列表,供用户进行选择;
列表是按照数字顺序排列的,我们只要选择数字即可;
一般来讲,select与case一起使用;
select是一个无限循环结构,因此,必须在循环体中使用break命令以退出循环,或者可以使用exit命令直接终止脚本运行;
select NAME [in 词语 ... ;] do 命令; done
select NAME [in LIST] ; do
命令
done
总结:
shell脚本编程语言:
过程式编程语言
顺序:主体结构
选择:
if
单
双
多
case
循环
for
遍历列表:
控制变量
while
until
select
编写个shell脚本将/usr/local/test目录下大于100k的文件转移到/tmp目录下。
#!/bin/bash
#
FPATH="/usr/local/test"
DEST=/tmp
for i in $FPATH/* ; do
if [ -f $i ] ; then
if [ $(wc -c < $i) -gt 102400 ] ; then
ls -l $i
fi
fi
done
回顾:
case
while
until
continue
break
select
systemV风格的服务管理脚本:
给脚本传递一些参数:start, stop, restart, status
myservice.sh
#!/bin/bash
#
lockfile="/var/lock/subsys/$(basename $0)"
case $1 in
start)
if [ -f $lockfile ] ; then
echo "服务已经启动...."
else
touch $lockfile
echo "服务正在启动...."
fi
;;
stop)
if [ -f $lockfile ] ; then
rm -f $lockfile
echo "服务已经停止...."
else
echo "服务尚未启动..."
fi
;;
restart)
if [ -f $lockfile ] ; then
rm -f $lockfile
echo "服务已经停止...."
else
echo "服务尚未启动..."
fi
if [ -f $lockfile ] ; then
echo "服务已经启动...."
else
touch $lockfile
echo "服务正在启动...."
fi
;;
status)
if [ -f $lockfile ] ; then
echo "服务已经启动...."
else
echo "服务已经停止...."
fi
;;
*)
echo "Usage: $(basename $0) start|stop|restart|status"
exit 5
;;
esac
把那些在脚本中重复出现并且没有任何改变的代码,封装起来,在适当的场景中调用执行;
程序员将这种被封装起来的代码称为功能体,或者叫模块;
function —— 函数
在shell脚本编程中,函数是由若干条shell命令组成的语句块;通常用于代码重用和模块化封装;
函数里面的内容和shell程序形式上是一致的;不同之处就是,shell代码可以直接被执行;而函数中的内容,不能独立执行,只有被调用的时候才执行;
函数是在shell程序的当前shell中运行的;
bash
bash script_file
function
定义函数:
函数是由两部分组成:
函数名称 + 函数体(能够实现独立功能的shell语句块)
语法一:
function func_name {
函数体
}
语法二:
func_name() {
函数体
}
注意:函数名和()之间不能加空白字符;
注意:函数可以在交互式环境下定义,也可以在脚本中定义;
函数的使用
函数在定义的时候,其函数体中包含的所有命令均不会被执行;只有函数被调用的时候,才会执行其中的命令语句;
调用方式:通过直接给出函数名称的方式调用;
有很多的函数是存放于专门用于保存函数的文件中;如果想要调用这样的文件中保存的函数,使用source命令(.)加载文件,然后再以直接给出函数名称的方式调用函数;
使用set命令可以查看所有当前shell中生效的函数;
使用unset命令可以撤销已经定义的函数;
函数的返回值:
两种返回值:
函数的执行结果的返回值:
1.在函数体中使用了echo或printf命令输出的结果;
2.在函数体中某些命令输出的结果;
函数的状态返回值:
1.函数中最后一条命令的执行状态返回值;
2.自定义退出状态码:
return [n]
n:0-255 (1 2 127尽可能不使用)
0: 表示无错误返回
1-255:有错误返回
注意:只要函数在执行时,遇到了return命令,不管函数中的命令语句是否全部执行完成,立刻退出函数;
函数的生命周期:
从被调用开始,到遇到return命令或全部的语句执行完成为止;
函数的实参
在函数体中,可以使用$1,$2,..位置变量为函数提供参数;还可以使用$*或$@的方式引用所有位置参数;还可以使用$#计算为函数传递的参数个数;
在调用函数的时候,直接在函数名称后面以空白字符分隔多个参数即可;比如:func_name arg1 arg2 ...
传递给函数参数的位置参数,是调用函数的时候,函数名称后面的以空白字符分隔的字符串序列;跟脚本的位置参数不是一回事;
变量:
shell中的变量为弱变量
1.无需事先声明
2.无需指定变量类型,默认为字符型
变量分类:
环境变量:
当前shell及子shell
本地变量:
当前shell
局部变量:
local VAR_NAME=VALUE
当前函数体
位置变量
特殊变量
建议:手动撤销自己定义或声明的所有变量;
函数的递归调用
简单来说,就是在函数体中调用函数自身;
阶乘:
N!=N*(N-1)!=N*(N-1)*(N-2)!=...=N*(N-1)*(N-2)*...*2*1
shell代码:
#!/bin/bash
# Author: Tianyu.Zhao
#
fact(){
if [ $1 -eq 0 ] || [ $1 -eq 1 ] ; then
echo 1
else
echo "$[$1*$(fact $[$1-1])]"
fi
}
echo -n "$1!="
fact $1
斐波那契数列(黄金分隔数列):
1 1 2 3 5 8 13 21 34 55 ...
假设兔子出生一个月之后才会有繁殖能力:
N=N-1 + N-2
shell代码:
#!/bin/bash
# Author: Tianyu.Zhao
#
fabonacci(){
if [ $1 -eq 1 ] ; then
echo 1
elif [ $1 -eq 2 ] ; then
echo 1
else
echo $[$(fabonacci $[$1-1])+$(fabonacci $[$1-2])]
fi
}
#列出所有的斐波那契数列的项
for I in `seq 0 $1` ; do
fabonacci $I
done
汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
利用函数,实现N片盘的汉诺塔的移动步骤
#!/bin/bash
# Author: Tianyu.Zhao
#
step=0
move(){
let step++
echo "$step: move disk $1 $2 -----> $3"
}
hanoi(){
if [ $1 -eq 1 ];then
move $1 $2 $4
else
hanoi "$[$1-1]" $2 $4 $3
move $1 $2 $4
hanoi "$[$1-1]" $3 $2 $4
fi
}
hanoi $1 A B C
文件abc.txt的内容如下:
2,3,4,5,6
B,c,d,e,f
6,7,8,9,10
f,g,h,i,j
写一个脚本,利用任一循环结构,输出每一行的第二个和第四个字符(以逗号分隔)
回顾:
shell脚本编程之函数
练习:
写一个脚本:
1.允许用户通过命令行传递参数,实现用户账户的管理;
2.如果给出-a|--add选项,就创建该选项后面的用户账户;
3.如果给出-d|--del选项,就删除该选项后面的用户账户;
4.如果用户给出-v|--verbose选项, 就显示删除或创建用户的信息;
5.如果用户给出-h|--help选项,就显示帮助信息,并且以0作为退出状态码退出脚本的运行;
6.如果用户给出其他选项,显示帮助信息,并以5作为退出状态码鬼畜脚本的运行;
#!/bin/bash
#Author: Link
#Description: administrate users
#5: no enough args
#6: error args
#
DEBUG=0
ADDUSER=0
DEUSER=0
usage(){
echo "Usage: $(basename $0) -a|--add user1,user2,... | -d|--del user1,user2,... | [-v|-verbose] | [-h|--help]"
echo
echo "Options: "
echo -e " -a, --add\vCreate user from list."
echo -e " -d, --del\vDelete user from list."
echo -e " -v, --verbose\vDisplay infomation for your operating."
echo -e " -h, --help\vDisplay this menu."
}
createuser() {
ADDUSER_LIST=$(echo $1 | tr ',' ' ')
for I in $ADDUSER_LIST ; do
if id $I &> /dev/null ; then
[ $DEBUG -eq 1 ] && echo "$I exists."
else
useradd $I &> /dev/null
echo $I | passwd --stdin $I &> /dev/null
[ $DEBUG -eq 1 ] && echo "Create $I successfully."
fi
done
}
deleteuser() {
DELUSER_LIST=$(echo $1 | tr ',' ' ')
for J in $DELUSER_LIST ; do
if id $J &> /dev/null ; then
userdel -r $J &> /dev/null
[ $DEBUG -eq 1 ] && echo "Delete $J finished."
else
[ $DEBUG -eq 1 ] && echo "$I not exists."
fi
done
}
if [ $# -le 0 ] ; then
usage
exit 5
fi
while [ $# -ne 0 ] ; do
case $1 in
-h|--help)
usage
exit
;;
-v|--verbose)
DEBUG=1
shift
;;
-a|--add)
ADDUSER=1
ALIST=$2
shift 2
;;
-d|--del)
DELUSER=1
DLIST=$2
shift 2
;;
*)
usage
exit 6
;;
esac
done
if [ $ADDUSER -eq 1 ] ; then
createuser $ALIST
fi
if [ $DELUSER -eq 1 ] ; then
deleteuser $DLIST
fi
总结:
写一个脚本的步骤:
1.构建程序主体
默认是顺序执行结构
根据需求添加相应的选择结构和循环结构
2.确定完成某功能所需的命令
3.调试(bash -x /PATH/TO/SCRIPT_FILE)
4.写明帮助信息