本章内容
条件判断
循环
信号捕捉
函数
数组
高级字符串操作
高级变量
Expect 流程控制
过程式编程语言:
顺序执行
选择执行
循环执行 条件选择if语句
选择执行:
注意:if语句可嵌套
单分支
if 判断条件;then
条件为真的分支代码
fi
双分支
if 判断条件; then
条件为真的分支代码
else
条件为假的分支代码
fi if 语句
多分支
if 判断条件1; then
条件为真的分支代码
elif 判断条件2; then
条件为真的分支代码
elif 判断条件3; then
条件为真的分支代码
else
以上条件都为假的分支代码
fi
逐条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if语句 If示例
根据命令的退出状态来执行命令
if ping -c1 -W2 station1 &> /dev/null; then
echo 'Station1 is UP'
elif grep "station1" ~/maintenance.txt &> /dev/null
then
echo 'Station1 is undergoing maintenance‘
else echo 'Station1 is unexpectedly DOWN!' exit 1
fi 条件判断:case语句
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
默认分支
;;
esac
case支持glob风格的通配符:
*: 任意长度任意字符
?: 任意单个字符
[]:指定范围内的任意单个字符
a|b: a或b 练习
1、编写脚本/root/bin/createuser.sh,实现如下功能:使用一个用户名做为参数,如果指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id号等信息
答:
#!/bin/bash
#FileName: createuser.sh
[ $# -ne 1 ] && echo "1 user" && exit
useradd $1 &> /dev/null
if [ $? -ne 0 ];then
echo "user $1 existed"
else
echo "user $1 added successfully"
getent passwd $1
fi
执行结果如下:
[root@centos7 bin]#./createuser.sh user1
user user1 added successfully
user1:x:1021:1022::/home/user1:/bin/bash
[root@centos7 bin]#./createuser.sh user1
user user1 existed
2、编写脚本/root/bin/yesorno.sh,提示用户输入yes或no,并判断用户输入的是yes还是no,或是其它信息
答:
#!/bin/bash
#FileName: yesorno.sh
read -p "Do you agree? yes or no: " ANS
if [[ $ANS =~ ^([Yy]([Ee][Ss])?)$ ]];then
echo "your name is yes"
elif [[ $ANS =~ ^([Nn][Oo]?)$ ]]:then
echo "your answer is no"
else
echo "Please input yes or no"
fi
执行结果如下
[root@centos7 bin]#./yesorno.sh
Do you agree? yes or no: y
your name is yes
[root@centos7 bin]#./yesorno.sh
Do you agree? yes or no: n
your answer is no
[root@centos7 bin]#./yesorno.sh
Do you agree? yes or no: a
Please input yes or no
3、编写脚本/root/bin/filetype.sh,判断用户输入文件路径,显示其文件类型(普通,目录,链接,其它文件类型)
答:
#!/bin/bash
#FileName: filetype.sh
read -p "Please input a filepath: " path
ls -ld $path &> /dev/null
if [ $? -ne 0 ];then
echo "The file is not existed"
else
type=`ls -ld $path | head -c1`
fi
case $type in
-)
echo "$path is a regular file"
;;
d)
echo "$path is a directory"
;;
b)
echo "$path is a block device file"
;;
c)
echo "$path is a character device file"
;;
l)
echo "$path is a symbolic link"
;;
p)
echo "$path is a named pipe"
;;
s)
echo "$path is a local socket file"
;;
*)
echo "error"
esac
执行结果如下:
[root@centos7 bin]#./filetype.sh
Please input a filepath: /etc/fstab
/etc/fstab is a regular file
[root@centos7 bin]#./filetype.sh
Please input a filepath: /root
/root is a directory
[root@centos7 bin]#./filetype.sh
Please input a filepath: /dev/sda
/dev/sda is a block device file
[root@centos7 bin]#./filetype.sh
Please input a filepath: /dev/zero
/dev/zero is a character device file
[root@centos7 bin]#./filetype.sh
Please input a filepath: /bin
/bin is a symbolic link
4、编写脚本/root/bin/checkint.sh,判断用户输入的参数是否为正整数
#!/bin/bash
#FileName: checkint.sh
read -p "Please input a positive integer: " INT
if [[ $INT =~ ^[0-9]+$ ]];then
echo "\"$INT\" is a positive integer"
else
echo "\"$INT\" is not a positive integer"
fi
执行结果如下:
[root@centos7 bin]#./checkint.sh
Please input a positive integer: 23
"23" is a positive integer
[root@centos7 bin]#./checkint.sh
Please input a positive integer: -16
"-16" is not a positive integer
[root@centos7 bin]#./checkint.sh
Please input a positive integer: abc
"abc" is not a positive integer 循环
循环执行
将某代码段重复运行多次
重复运行多少次
循环次数事先已知
循环次数事先未知
有进入条件和退出条件
for, while, until for循环
for 变量名 in 列表;do
循环体
done
执行机制:
依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束
列表生成方式:
(1) 直接给出列表
(2) 整数列表:
(a) {start..end}
(b) $(seq [start [step]] end)
(3) 返回列表的命令
$(COMMAND)
(4) 使用glob,如:*.sh
(5) 变量引用;
$@, $*
例:for file in `ls /boot`; do echo The filename is $file; done 练习:用for实现
1、判断/var/目录下所有文件的类型
#!/bin/bash
#FileName: filetype_var.sh
for path in `ls -d /var/*`;do
type=`ls -ld $path | head -c1`
case $type in
-)
echo "$path is a regular file"
;;
d)
echo "$path is a directory"
;;
b)
echo "$path is a block device file"
;;
c)
echo "$path is a character device file"
;;
l)
echo "$path is a symbolic link"
;;
p)
echo "$path is a named pipe"
;;
s)
echo "$path is a local socket file"
;;
*)
echo "error"
;;
esac
done
执行结果如下:
[root@centos7 bin]#./filetype_var.sh
/var/account is a directory
/var/adm is a directory
/var/cache is a directory
/var/crash is a directory
/var/db is a directory
/var/empty is a directory
/var/games is a directory
/var/gopher is a directory
/var/kerberos is a directory
/var/lib is a directory
/var/local is a directory
/var/lock is a symbolic link
/var/log is a directory
/var/mail is a symbolic link
/var/nis is a directory
/var/opt is a directory
/var/preserve is a directory
/var/run is a symbolic link
/var/spool is a directory
/var/target is a directory
/var/tmp is a directory
/var/www is a directory
/var/yp is a directory
2、添加10个用户user1-user10,密码为8位随机字符
#!/bin/bash
#FileName: createusers.sh
for num in {1..10};do
useradd user$num &> /dev/null
if [ $? = 0 ];then
echo user$num created successfully
echo `openssl rand -base64 6` | passwd --stdin user$num &> /dev/null
else
echo user$num existed
fi
done
执行结果如下:
[root@centos7 bin]#./createusers.sh
user1 created successfully
user2 created successfully
user3 created successfully
user4 created successfully
user5 created successfully
user6 created successfully
user7 created successfully
user8 created successfully
user9 created successfully
user10 created successfully
3、/etc/rc.d/rc3.d目录下分别有多个以K开头和以S开头的文件;分别读取每个文件,以K开头的输出为文件加stop,以S开头的输出为文件名加start,如K34filename stop S66filename start
#!/bin/bash
#FileName: rc3.d_KS.sh
for i in `ls /etc/rc.d/rc3.d`;do
if [[ $i =~ ^K.*$ ]];then
echo $i stop;
elif [[ $i =~ ^S.*$ ]];then
echo $i start;
else
:
fi
done
[root@centos7 bin]#./rc3.d_KS.sh
K50netconsole stop
S10network start
4、编写脚本,提示输入正整数n的值,计算1+2+…+n的总和
#!/bin/bash
#FileName: sum_n.sh
while true; do
read -p "Please input a positive integer: " n
[[ $n =~ ^[1-9][0-9]*$ ]] && break
done
sum=0
for i in `seq 1 $n`;do
sum=$[sum+i]
done
echo "n=$n"
echo "1+2+...+$n=$sum"
执行结果如下:
[root@centos7 bin]#./sum_n.sh
Please input a positive integer: 100
n=100
1+2+...+100=5050
5、计算100以内所有能被3整除的整数之和
#!/bin/bash
#FileName: sum_%3=0.sh
sum=0
for i in {0..100};do
if [ $[i%3] -eq 0 ];then
sum=$[sum+i]
else
:
fi
done
echo sum=$sum
执行结果如下:
[root@centos7 bin]#./sum_%3\=0.sh
sum=1683
6、编写脚本,提示请输入网络地址,如192.168.0.0,判断输入的网段中主机在线状态
#!/bin/bash
#FileName: scanip.sh
# 提示输入网络地址,用正则表达式判断网段合法性
while true; do
read -p "Please input an ip net, such as 192.168.0.0, the script will scan ip net 192.168.0.0/24: " address
if [[ $address =~ ^(([1-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]];then
echo -e "ip is legal, start scanning...\n"
break
else
echo -e "wrong ip address\n"
fi
done
# 开始扫描
net=`echo $address | sed -r 's/^(.*)\..*$/\1/'`
for i in {1..254};do {
if ping -c1 -w1 $net.$i &> /dev/null ;then
echo "$net.$i is up"
else
echo "$net.$i is down"
fi } &
done
wait
执行结果如下:
[root@centos7 bin]#./scanip.sh
Please input an ip net, such as 192.168.0.0, the script will scan ip net 192.168.0.0/24: 192.168.0.0
ip is legal, start scanning...
192.168.0.1 is up
192.168.0.103 is up
192.168.0.104 is up
192.168.0.113 is up
192.168.0.114 is up
192.168.0.5 is down
192.168.0.2 is down
……
……
192.168.0.177 is down
192.168.0.245 is down
192.168.0.232 is down
192.168.0.228 is down
7、打印九九乘法表
#!/bin/bash
#Filename: 9×9.sh
for i in {1..9};do
for j in `seq 1 $i`;do
result=$[j*i]
echo -e "${j}×${i}=$result\t\c"
done
echo
done
执行结果如下:
8、在/testdir目录下创建10个html文件,文件名格式为数字N(从1到10)加随机8个字母,如:1AbCdeFgH.html
#!/bin/bash
#Filename: touch_Nrand.html.sh
mkdir /testdir/ &> /dev/null
rand=0
for i in {1..10};do
rand=`openssl rand -base64 20 | tr -dc [[:alpha:]] | head -c8`
touch "/testdir/$i$rand.html"
done
执行结果如下:
[root@centos7 bin]#./touch_Nrand.html.sh
[root@centos7 bin]#cd /testdir
[root@centos7 testdir]#ls
10InrYzLKS.html 3rlecXfCR.html 6MjgPCXtX.html 9qQGRIxir.html
1maGPaaSn.html 4LZssBMjM.html 7ImLLSRih.html
2vjaOmtIO.html 5goBpJYRs.html 8OXhFdlTZ.html
9、打印等腰三角形
#!/bin/bash
#Filename: triangle.sh
while true;do
read -p "Please input the high of the triangle: " high
[[ $high =~ ^[0-9]+$ ]] && break
done
for i in `seq $high`;do
for j in `seq $[high-i]`;do echo -e " \c";done
for k in `seq $[2*i-1]`;do echo -e "*\c";done
echo
done
执行结果如下:
while循环
while CONDITION; do
循环体
done
CONDITION:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为“true”,则执行一次循环;直到条件测试状态为“false”终止循环
因此:CONDTION一般应该有循环控制变量;而此变量的值会在循环体不断地被修正
进入条件:CONDITION为true
退出条件:CONDITION为false 练习:用while实现
1、编写脚本,求100以内所有正奇数之和
#!/bin/bash
#FileName: while_sum.sh
i=0
sum=0
while [ $i -le 100 ] ;do
if [ $[i%2] -eq 1 ];then
sum=$[sum+i]
fi
let i++
done
echo sum=$sum
执行结果如下:
[root@centos7 bin]#./while_sum.sh
sum=2500
2、编写脚本,提示请输入网络地址,如192.168.0.0,判断输入的网段中主机在线状态,并统计在线和离线主机各多少
#!/bin/bash
#FileName: scanup.sh
# 提示输入网络地址,用正则表达式判断网段合法性
while true; do
read -p "Please input an ip net, such as 192.168.0.0, the script will scan
ip net 192.168.0.0/24: " address
if [[ $address =~ ^(([1-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([1-
9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$ ]];then
echo -e "ip is legal, start scanning...\n"
break
else
echo -e "wrong ip address\n"
fi
done
# 开始扫描
net=`echo $address | sed -r 's/^(.*)\..*$/\1/'`
i=1
touch ./scanup_tmp
while [ $i -le 254 ];do {
if ping -c1 -w1 $net.$i &> /dev/null ;then
echo "$net.$i is up" | tee -a ./scanup_tmp
else
echo "$net.$i is down" | tee -a ./scanup_tmp
fi } &
let i++
done
wait
up=`grep up ./scanup_tmp | wc -l`
down=`grep down ./scanup_tmp | wc -l`
rm -f ./scanup_tmp &> /dev/null
echo
echo up=$up
echo down=$down
# 扫描结束
执行结果如下:
[root@centos7 bin]#./scanup.sh
Please input an ip net, such as 192.168.0.0, the script will scan ip net 192.168.0.0/24: 192.168.0.0
ip is legal, start scanning...
192.168.0.1 is up
192.168.0.104 is up
192.168.0.103 is up
192.168.0.5 is down
……
192.168.0.243 is down
192.168.0.251 is down
192.168.0.247 is down
up=6
down=248
3、编写脚本,打印九九乘法表
#!/bin/bash
#FileName: 9×9_while.sh
declare -i i=1
while [ $i -le 9 ];do
declare -i j=1
while [ $j -le $i ];do
result=$[j*i]
echo -e "${j}×${i}=$result\t\c"
let j++
done
echo
let i++
done
执行结果如下:
4、编写脚本,利用变量RANDOM生成10个随机数字,输出这个10数字,并显示其中的最大值和最小值
#!/bin/bash
#FileName: 10_random_num.sh
declare -i rand min max i=1
echo "10 random numbers: "
while [ $i -le 10 ]; do
rand=$RANDOM
echo $i $rand
[ $i -eq 1 ] && { min=$rand; max=$rand; }
[ "$rand" -lt "$min" ] && min=$rand
[ "$rand" -gt "$max" ] && max=$rand
let i++
done
echo min $min
echo max $max
执行结果如下:
[root@centos7 bin]#./10_random_num.sh
10 random numbers:
1 1309
2 23514
3 21965
4 6954
5 25446
6 19996
7 16356
8 4571
9 32208
10 8522
min 1309
max 32208
5、编写脚本,实现打印国际象棋棋盘
#!/bin/bash
#FileName: chess.sh
# 给国际象棋棋盘选2种颜色
# 打印8种颜色
echo "Please select 2 colour for the chessboard: "
for i in {0..7};do
echo -e "$[i+1]\033[1;4${i}m \033[0m \c"
done
echo -e "\n"
# 选择颜色1
while true;do
read -p "Select the first colour (1-8): " C1
[[ "$C1" =~ ^[1-8]$ ]] && break
done
echo -e "The first colour is $C1\033[1;4$[C1-1]m \033[0m\n"
# 选择颜色2
while true;do
read -p "Select the second colour (1-8): " C2
[[ "$C2" =~ ^[1-8]$ ]] && break
done
echo -e "The second colour is $C2\033[1;4$[C2-1]m \033[0m\n"
# 选择棋盘大小
while true;do
read -p "Now select the> [[ "$S" =~ ^[1-9]$ ]] && break
done
# 打印棋盘
echo "Print the chessboard: "
echo
for i in `eval echo {0..$[8*S-1]}`;do
for j in `eval echo {0..$[8*S-1]}`;do
if [ $[(i/S+j/S)%2] -eq 0 ];then
echo -e "\033[1;4$[C1-1]m \033[0m\c"
else
echo -e "\033[1;4$[C2-1]m \033[0m\c"
fi
done
echo
done
# 打印结束
执行结果如下:
6、后续六个字符串:efbaf275cd、4be9c40b8b、44b2395c46、f8c8873ce0、b902c16c8b、ad865d2f63是通过对随机数变量RANDOM随机执行命令: echo $RANDOM|md5sum|cut -c1-10 后的结果,请破解这些字符串对应的RANDOM值
#!/bin/bash
#FileName: rand_md5sum.sh
declare -i i=0
declare md5
while [ $i -le 32767 ]; do {
md5=`echo $i | md5sum | cut -c1-10`
case $md5 in
efbaf275cd)
echo "efbaf275cd ---> $i" | tee -a ./md5sum.txt
;;
4be9c40b8b)
echo "4be9c40b8b ---> $i" | tee -a ./md5sum.txt
;;
44b2395c46)
echo "44b2395c46 ---> $i" | tee -a ./md5sum.txt
;;
f8c8873ce0)
echo "f8c8873ce0 ---> $i" | tee -a ./md5sum.txt
;;
b902c16c8b)
echo "f8c8873ce0 ---> $i" | tee -a ./md5sum.txt
;;
ad865d2f63)
echo "f8c8873ce0 ---> $i" | tee -a ./md5sum.txt
;;
*)
;;
esac } &
let i++
done
wait
cat ./md5sum.tx
脚本结束,输出结果如下:
f8c8873ce0 ---> 1000
f8c8873ce0 ---> 3000
f8c8873ce0 ---> 6000
44b2395c46 ---> 9000
4be9c40b8b ---> 12000
efbaf275cd ---> 15000 until循环
until CONDITION; do
循环体
done
进入条件: CONDITION 为false
退出条件: CONDITION 为true 循环控制语句continue
用于循环体中
continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
continue
fi
CMDn
...
done 循环控制语句break
用于循环体中
break [N]:提前结束第N层循环,最内层为第1层
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
break
fi
CMDn
...
done 循环控制shift命令
shift [n]
用于将参量列表 list 左移指定次数,缺省为左移一次。
参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 循环遍历位置参量列表时,常用到 shift
./doit.sh a b c d e f g h
./shfit.sh a b c d e f g h 示例:doit.sh
#!/bin/bash
# Name: doit.sh
# Purpose: shift through command line arguments
# Usage: doit.sh [args]
while [ $# -gt 0 ] # or (( $# > 0 ))
do
echo $*
shift
done 示例:shift.sh
#!/bin/bash
#step through all the positional parameters
until [ -z "$1" ]
do
echo "$1"
shift
done
echo 创建无限循环
while true; do
循环体
done
until false; do
循环体
Done 练习
1、每隔3秒钟到系统上获取已经登录的用户的信息;如果发现用户hacker登录,则将登录时间和主机记录于日志/var/log/login.log中,并退出脚本
#!/bin/bash
#FileName: hacker_login.sh
until false; do
if who | grep "^hacker\ " &> /dev/null; then
who | grep "^hacker\ " >> /var/log/login.log
break
fi
sleep 3
done
如果想让脚本在后台运行,可输入nohup ./hacker_login.sh &> /dev/null &
hacker用户登录后脚本退出,执行结果如下:
[root@centos7 bin]#cat /var/log/login.log
hacker pts/1 2018-05-10 16:20 (192.168.30.1)
2、随机生成10以内的数字,实现猜字游戏,提示比较大或小,相等则退出
#!/bin/bash
#FileName: guess.sh
num=$[RANDOM%11]
while true;do
read -p "Please guess the number between 0 and 10: " guess
if [[ ! $guess =~ [[:digit:]]+ ]];then
:
elif [ $guess == $num ];then
echo "You are right! The number is ${num}!"
break
elif [ $guess -gt $num ];then
echo "too large"
elif [ $guess -lt $num ];then
echo "too small"
else
:
fi
done
执行结果如下:
[root@centos7 bin]#./guess.sh
Please guess the number between 0 and 10: aaa
Please guess the number between 0 and 10: 5
too large
Please guess the number between 0 and 10: 3
too large
Please guess the number between 0 and 10: 1
You are right! The number is 1!
3、用文件名做为参数,统计所有参数文件的总行数
#!/bin/bash
#FileName: sum_line.sh
[ $# -eq 0 ] && echo "No args" && exit
declare sum=0
until [ -z $1 ]; do
if [ -e $1 ]; then
sum=$[sum+`cat $1 | wc -l`]
else
echo "file '$1' is not existed"; exit
fi
shift
done
echo "There are all $sum cows."
执行结果如下:
[root@centos7 bin]#./sum_line.sh /etc/fstab /etc/passwd
There are all 70 cows.
4、用二个以上的数字为参数,显示其中的最大值和最小值
#!/bin/bash
#FileName: max_and_min.sh
[ $# -lt 2 ] && echo "at least 2 args" && exit
min=$1
max=$1
until [ -z $1 ]; do
[[ ! $1 =~ ^-?[0-9]+$ ]] && echo $1 is not a number && exit
[ $1 -lt $min ] && min=$1
[ $1 -gt $max ] && max=$1
shift
done
echo min number is $min
echo max number is $max 特殊用法
while循环的特殊用法(遍历文件的每一行):
while read line; do
循环体
done < /PATH/FROM/SOMEFILE
依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line
练习
扫描/etc/passwd文件每一行,如发现GECOS字段为空,则填充用户名和单位电话为62985600,并提示该用户的GECOS信息修改成功
双小括号方法,即((…))格式,也可以用于算术运算
双小括号方法也可以使bash Shell实现C语言风格的变量操作
I=10
((I++))
for循环的特殊格式:
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
循环体
done
控制变量初始化:仅在运行到循环代码段时执行一次
控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断
例:
for (( i=1,sum=0;i [[ $n =~ ^[1-9][0-9]?+$ ]] && break
done
n=$[n-1]
# 生成并打印矩阵
for i in `eval echo {0..$n}`; do
for j in `eval echo {0..$n}`; do
matrix[$i$j]=$[i*(n+1)+j+1]
echo -e "${matrix[$i$j]} \c"
done
echo
done
# 开始转置矩阵
echo -e "\nswitch the matrix: \n"
declare tmp
for i in `eval echo {1..$n}`; do
for j in `eval echo {0..$[i-1]}`; do
tmp=${matrix[$i$j]}
matrix[$i$j]=${matrix[$j$i]}
matrix[$j$i]=$tmp
done
done
for i in `eval echo {0..$n}`; do
for j in `eval echo {0..$n}`; do
echo -e "${matrix[$i$j]} \c"
done
echo
done
输出结果如下:
[root@centos7 bin]#./matrix.sh