shell脚本编程进阶
在linux shell中,通常我们将一些命令写在一个文件中就算是一个shell脚本了,但是如果需要执行更为复杂的逻辑判断,我们就需要使用流程控制语句来支持了。
所谓流程控制既是通过使用流程控制语句对程序流程的选择、循环、转向和返回等进行控制。流程控制是所有编程语言分重要组成部分,linux shell同样有一套自己的流程控制语句,其中主要包括条件语句(if),循环语(for,while),选择语句(case)。
条件选择if语句
单分支
if判断条件;then
条件为真的分支代码
fi
双分支
if 判断条件; then
条件为真的分支代码
else
条件为假的分支代码
fi
多分支
if 判断条件1; then
条件为真的分支代码
elif 判断条件2; then
条件为真的分支代码
elif 判断条件3; then
条件为真的分支代码
else
以上条件都为假的分支代码
fi
逐个条件进行判断,第一次为“真”条件时,接着执行其分支,而后结束整个if语句
条件判断case语句
case 变量引用 in #变量的引用加$
PAT1)
分支1
;;
PAT2)
分支2
;;
...
\*)
默认分支
;;
esac
case支持glob风格的通配符:
*: 任意长度任意字符
?: 任意单个字符
[]:指定范围内的任意单个字符
a|b: a或b
练习
1、编写脚本/root/bin/createuser.sh,实现如下功能:使用一个用户名做为参数,如果
指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id号等信息
#!/bin/bash
read -p "please input username:" username
useradd $username &> /dev/null
if [ $? -eq 0 ];then
echo "add $username user" && id $username
else
echo "the user already exits"
fi
2、编写脚本/root/bin/yesorno.sh,提示用户输入yes或no,并判断用户输入的
是yes还是no,或是其它信息
#!/bin/bash
read -p "Do you agree? yes or no: " ANS
if [ -z "$ANS" ] ;then
echo "Please input yes or no"
exit
fi
case $ANS in
[Yy]|[Yy][Ee][Ss])
echo "Your answer is YES"
;;
[Nn]|[Nn][Oo])
echo "Your answer is NO"
;;
*)
echo "Your answer is fales"
esac
3、编写脚本/root/bin/filetype.sh,判断用户输入文件路径,显示其文件类型
(普通,目录,链接,其它文件类型)
#!/bin/bash
read -p "input file or dir:" choose
[ -z "$choose" ] && echo "Please input a file or dir"
if [ -d "$choose" ] ;then
echo "$choose is directory"
elif [ -L "$choose" ] ;then
echo "$choose is a link"
elif [ -f "$choose" ] ;then
echo "$choose is a common file"
else
echo "$choose is other type"
fi
4、编写脚本/root/bin/checkint.sh,判断用户输入的参数是否为正整数
#!/bin/bash
read -p "input a digit:" NUM
if [[ "$NUM" =~ ^[0-9]+$ ]] ;then
echo "$NUM is a int"
fi
循环
循环执行
将某代码段重复运行多次
重复运行多少次
循环次数事先已知
循环次数事先未知
有进入条件和退出条件
for, while, until
for循环
for 变量名 in 列表;
do
循环体
done
执行机制:
依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直 到列表中的元素耗尽,循环结束
列表生成方式:
(1) 直接给出列表
for num in 1 2 3 4 5;do echo The number is $num ;done
The number is 1
The number is 2
The number is 3
The number is 4
The number is 5
(2) 整数列表:
(a) {start..end}
for num in {10..1..3};do echo The numberis $num ;done
The numberis 10
The numberis 7
The numberis 4
The numberis 1
(b) $(seq [start [step]] end)
for num in `seq 2 4 10`;do echo The number is $num ;done
The number is 2
The number is 6
The number is 10
(3) 返回列表的命令 $(COMMAND)
(4) 使用glob,如:*.sh
for num in "/root/bin/*.sh";do echo The filename is $num ;done
The filename is /root/bin/*.sh
(5) 变量引用; $@独立, $*整体(位置变量)
多行重定向
练习:
用for实现
1、打印国际象棋
for i in {1..8};do
for j in {1..4};do
if [ $[i%2] -eq 0 ];then
echo -e "\033[1;41m \033[0m\e[1;43m \e[0m\c"
else
echo -e "\e[1;43m \e[0m\e[1;41m \e[0m\c"
fi
done
echo
done
\c=-n默认不转行
\e \e[0m=\033 \033[0m颜色提示符
2、添加10个用户user1-user10,密码为magedu
for i in {1..10};do
if id $i &> /dev/null ;then
echo "user$i is exist"
else
useradd user$i &>/dev/null
echo "magedu" | passwd --stdin user$i &>/dev/null
echo "user $i add successful"
fi
done
注:批量删除用户for i in {1..10};do userdel -r(删除家目录) user$i;done
3、/etc/rc.d/rc3.d目录下分别有多个以K开头和以S开头的文件;分别读取每个文件,
以K开头的输出为文件加stop,以S开头的输出为文件名加start,如K34filename stop
S66filename start
#!/bin/bash
for i in $(ls /etc/rc.d/rc3.d/);do
if [[ $i =~ ^K.* ]];then
echo "$i stop"
elif [[ $i =~ ^S.* ]];then
echo "$i start"
fi
done
4、编写脚本,提示输入正整数n的值,计算1+2+…+100的总和
#!/bin/bash
declare -i sum=0 n=100
for i in {1..100};do
let sum=sum+i | sum+=i
done
echo sum=$sum
unset sum
5、计算100以内所有能被3整除的整数之和
#!/bin/bash
sum=0
for i in {1..100};do
if [ $[$i%3] -eq 0 ];then
let sum+=$i
fi
done
echo $sum
6、编写脚本,提示请输入网络地址,如192.168.0.0,判断输入的网段中主机在线状态
net=192.168.140
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
7、打印九九乘法表
#!/bin/bash
for i in {1..9};do
for j in `seq 1 $i`;do
result=$[$j*$i]
echo -e "$j*$i=$result \c"
done
echo
done
8、在/testdir目录下创建10个html文件,文件名格式为数字N(从1到10)加随机8个字
母,如:1AbCdeFgH.html
取随机字母openssl rand -base64 20 | tr -dc '[:alpha:]' | head -c8
9、打印等腰三角形
*的个数=line*2-1
空格=总line-当前line
#!/bin/bash
read -p "Please input lines:" L
for i in `seq ${L}`;do
for (( j=${L};j>i;j-- ));do
echo -n " "
done
for j in `seq 1 $i`;do
echo -n "*"
done
for (( n=1;n<i;n++ ));do
echo -n "*"
done
echo " "
done
打印等腰三角形并且颜色随机
#!/bin/bash
read -p "Please input lines:" L
for i in `seq ${L}`;do
for j in `seq $[$L-i]`;do
echo -n " "
done
for k in `seq $[i*2-1]`;do
let color=$RANDOM%7+31
echo -ne "\e[1;5;${color}m*\e[0m"
done
echo
done
while循环
while 条件;do
循环体
done
条件(CONDITION):循环控制条件;进入循环之前,先做一次判断;每次循环之后会再次做判断;条件为"ture",
则执行一次循环;直到条件测试状态为“false”终止循环。
因此:CONDTION一般应该有循环控制变量;而此变量的值会在循环体不断地 被修正
进入条件:CONDITION为true
退出条件:CONDITION为false
练习:
1、编写脚本,求100以内所有正奇数之和
#!/bin/bash
declare -i sum=0
i=1
while [ $i -le 100 ];do
let sum=sum+i
let i=i+2
done
echo sum=$sum
2、编写脚本,提示请输入网络地址,如192.168.0.0,判断输入的网段中主机
在线状态,并统计在线和离线主机各多少
#!/bin/bash
net=192.168.140
let num=1
x=0(up的主机)
y=0(down的主机数)
while [ ${num} -le 254 ];do
ping -c1 -w1 "${net}.${num}" &>/dev/null
if [ $? -eq 0 ];then
echo "${net}.${num} is up"
x=$(($x+1))
else
echo "${net}.${num} is down"
y=$(($y+1))
fi
num=$(($num+1))
done
echo "total ${x} hosts are up"
echo "total ${y} hosts are down "
3、编写脚本,打印九九乘法表
#!/bin/bash
declare -i i=1
while [ $i -le 9 ];do
declare -i j=1
while [ $j -le $i ];do
result=$[$i*$j]
echo -e "${j}x${i}=$result\t\c" \t是产生空格并对齐;\c是不换
let j++
done
echo
let i++
done
4、编写脚本,利用变量RANDOM生成10个随机数字,输出这个数字,并显
示其中的最大值和最小值
#!/bin/bash
declare -i a=0
max=0
min=$RANDOM
while [ $a -le 9 ];do
num=$RANDOM
if [ $num -le $max ];then
if [ $num -le $min ];then
min=$num
fi
else
max=$num
fi
let a++:q!:
done
echo "the min number is $min,the max number is $max"
5、编写脚本,实现打印国际象棋棋盘
#!/bin/bash
let i=0
while [ $i -le 15 ];do
for ((j=0;j<4;j++));do
if [ $[i%4] -eq 0 -o $[i%4] -eq 1 ];then
echo -e "\033[1;41m \033[0m\033[1;47m \033[0m\c"
else
echo -e "\033[1;47m \033[0m\033[1;41m \033[0m\c"
fi
done
echo
let i++
done
6、后续六个字符串:efbaf275cd、4be9c40b8b、44b2395c46、
f8c8873ce0、b902c16c8b、ad865d2f63是通过对随机数变量RANDOM随机
执行命令: echo $RANDOM|md5sum|cut –c1-10 后的结果,请破解这些
字符串对应的RANDOM值while循环
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
循环控制语句brek
用于循环体中
break [N]:提前结束第N层循环,最内层为第1层
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
break
fi
CMDn
...
done
创建无限循环
while true; do
循环体
done
until false; do
循环体
Done
练习
1、每隔3秒钟到系统上获取已经登录的用户的信息;如果发现用户hacker登录,
则将登录时间和主机记录于日志/var/log/login.log中,并退出脚本
#!/bin/bash
until `who | grep -o "^usr\b" &>/dev/null`;do
sleep 3
done
echo "user is logining"
who | grep "user" >> /data/login.sh:q!
2、随机生成10以内的数字,实现猜字游戏,提示比较大或小,相等则退出
#!/bin/bash
i=$[$RANDOM%11]
while read -p "please input a number:" a;do
[[ $a =~ ^[0-9]+$ ]] || { echo "please input a dight:1-10";continue; }
if [ $i -gt $a ];then
echo little
elif [ $i -lt $a ];then
echo more
else
echo right
break
fi
done
3、用文件名做为参数,统计所有参数文件的总行数
if [ $# -eq 0 ];then $#是所有未知变量
echo "please input file path"
else
until [ -z "$1" ];do -z判断文件是否为空
echo "tongji file:$1"
echo "the file total line are: `wc -l $1 | cut -d " " -f1`“
echo
shift
done
echo "finish"
fi
4、用二个以上的数字为参数,显示其中的最大值和最小值
函数:function
是由若干条shell命令组成的语句块,实现代码重用和模块化编程;函数只有被调用才会执行
语法1:
function func_name {
函数体
}
语法2::
func_name() {
函数体
}
注意:函数中变量的生效范围是当前所在shell
函数的生命周期:被调用时创建,返回时终止
函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用,调用函数时就像使用命令一样调用
return #:在函数中使用,跳出函数,返回值 #;如果没有定义退出返回值则函数的默认返回值为函数中最后一条命令的返回值
local var:声明函数为本地变量,只在函数内部生效
declare
-f func_name 查看指定函数
-xf func_name 声明函数的生效范围为全局 == export -f func_name
-i 声明变量为数字
-g 在函数中使用,声明变量为普通变量
declare 在函数中用隐式使用了local,定义的变量为本地变量
函数的递归调用:在函数中调用函数本身
fork炸弹
bome() { bome|bome& }; bome
编写脚本copycmd.sh
- (1) 提示用户输入一个可执行命令名称;
- (2) 获取此命令所依赖到的所有库文件列表;
- (3) 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下;
- 如:/bin/bash ==> /mnt/sysroot/bin/bash
- (4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下;(5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出;
- 如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2
#!/bin/bash
. my_functions
Ddir="/mnt/sysroot"
[ ! -d "$Ddir" ] && mkdir "$Ddir"
cpbin() {
cmddir=`echo "$Cmd_path" |grep -o ".*/\b"`
[ ! -d "${Ddir}${cmddir}" ] && mkdir -p "${Ddir}${cmddir}"
cp -n "$Cmd_path" "${Ddir}${cmddir}"
}
libcp() {
echo "$Lib_path" |while read line ;do
libdir=`echo "$line" |grep -o ".*/\b"`
[ ! -d "${Ddir}${libdir}" ] && mkdir -p "${Ddir}${libdir}"
cp -n "$line" "${Ddir}${libdir}" 2>/dev/null
done
}
while read -p "Input a cmd. (quit): " CMD ;do
if [ "$CMD" == 'quit' ] ;then echo "Think you! Bye bye."; break; fi
if ! which "$CMD" &>/dev/null ;then
echo "not find $CMD, please input again."
failed "copy $CMD failed."
continue
fi
Cmd_path=`which $CMD |grep -o "/.*"`
Lib_path=`ldd $Cmd_path |sed -nr 's#.*[[:space:]]+(/.*) .*#\1#p'`
cpbin
libcp
success "copy $CMD Complete."
done
求斐波那契数列的第N个数
#!/bin/bash
feibo() {
if [ $1 -eq 0 ] ; then
echo 0
elif [ $1 -eq 1 ] ;then
echo 1
else
echo $[`feibo $[$1-1]`+`feibo $[$1-2]`]
fi
}
read -p "please a num: " n
求N的阶乘
#!/bin/bash
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ] ;then
echo 1
else
echo $[$1*`fact $[$1-1]`]
fi
}
fact $1