bash脚本编程:
过程式编程语言
顺序执行
选择执行
循环执行
循环执行:必须有进入条件和退出条件
-
函数:结构化编程及代码重用
function
选择执行:
if 判断条件;then
条件为真的分支代码
fi
if 判断条件;then
添加为真的分支代码
else
条件为假的分支代码
fi
eg:
新建用户
#!/bin/bash
## 如果脚本参数小于1,则退出
if [ $# -lt 1 ];then
echo "At least one argument"
exit 1
fi
## 判断$1用户是否存在,用户不存在则创建
if id $1 &> /dev/null;then
echo "$1 exists"
exit 0
else
useradd $1
[ $? -eq 0 ] && echo "$1" | passwd --stdin $1 &> /dev/null || exit 1
fi
多分支
if 判断条件 1 ; then
条件为真的分支代码
elif 判断条件 2 ; then
条件为真的分支代码
elif 判断条件 3 ; then
条件为真的分支代码
else
以上条件都为假的分支代码
fi
- 逐条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if
循环
- 循环执行
将某代码段重复运行多次
重复运行多少次:
循环次数事先已知
循环次数事先未知
有进入条件和退出条件
- for, while, until
for循环语法:
for NAME in LIST;do
循环体
done
-
执行机制
依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体;直到列表中的元素耗尽,循环结束 -
循环列表生成方式:
(1)直接给出列表
(2)整数列表
{start..end}
$(seq [start [step]]end)
(3)glob(统配路径下的所有文件)如:*.sh
/etc/rc.d/rc3.d/k*
(4)返回列表的命令
$(COMMAND)
(5)变量引用:
$@,$*
eg:
通过ping命令探测172.17.250.1-254范围内的所有主机的在线状态
#!/bin/bash
net='172.17.253'
uphost=0
downhost=0
for i in {1..20};do
ping -c 1 -v 1 ${net}.$i &> /dev/null
if [ $? -eq 0 ];then
echo "${net}.$i is up"
let uphost++
else
echo "${net}.$i is down"
let downhost++
fi
done
echo "Up host:$uphost"
echo "Down host:$downhost"
while循环:
while CONDITON;do
循环体
done
CONDITION:循环控制列表:进入循环之前,先做一次判断;每一次循环之后会再次做判断
条件为“true”,则执行一次循环:直到条件测试状态为“false”终止循环;
因此:CONDITION一般应该有循环控制变量,而此变量的值会在循环体不断的被修正
进入条件:CONDITION 为true
退出条件:CONDITION 为false
示例
1 . 求100以内所有正整数之和
declare -i sum=0
declare -i i=1
while [ $i -le 100 ];do
let sum+=$i
let i++
done
echo "i=$i"
echo "Summary:$sum"
2 . 添加10个用户
declare -i i=1
declare -i users=0
while [ $i -le 1 ];do
if ! id user$i &> /dev/null;then
useradd user$i
echo "Add user:user$i"
let users++
fi
let i++
done
echo "Add $users users"
3 . 通过ping命令探测172.17.253.1-254范围的所有主机的在线状态:(用while循环)
declare -i i=1
declare -i uphosts=0
declare -i downhosts=0
net='172.17.253'
while [ $i -le 20 ];do
if ping -c 1 -w 1 $net.$i &> /dev/null;then
echo "$net.$i is up"
let uphosts++
else
echo "$net.$i is downhosts"
let downhosts++
fi
let i++
done
echo "Up hosts:$uphosts"
echo "Down host:$downhosts"
4 . 打印九九乘法表:(分别使用for和while循环实现)
for语句
for j in {1..9};do
for i in $(seq 1 $j);do
echo -n -e "${i}X${j}=$[$i*$j]\t"
done
echo
done
while语句
declare -i i=1
declare -i j=1
while [ $j -le 9 ];do
while [ $i -le $j ];do
echo -e -n "${i}X${j}=$[$i*$j]\t"
let i++
done
echo
let i=1
let j++
done
5 . 利用RANDOM生成10个随机数字,输出这10个数字,并显示其中的最大值和最小值
#!/bin/bash
declare -i max=0
declare -i min=0
declare -i i=1
#max=$rand
#min=$rand
while [ $i -le 9 ];do
rand=$RANDOM
echo $rand
if [ $i -eq 1 ];then
max=$rand
min=$rand
fi
if [ $rand -gt $max ];then
max=$rand
fi
if [ $rand -lt $min ];then
min=$rand
fi
let i++
done
echo "MAX:$max"
echo "MIN:$min"
unil
unil COMDITION;do
循环体
done
循环条件为:false
退出条件为:true
示例
1 . 求100以内所有正整数之和
/bin/bash
#!
declare -i i=1
declare -i sum=0
until [ $i -gt 100 ];do
let sum+=$i
let i++
done
echo "Sum:$sum"
2 . 打印九九乘法表
#!/bin/bash
# filename timetale3.sh
# author:danran
# time is 2017-06-22
declare -i j=1
declare -i i=1
until [ $j -gt 9 ];do
until [ $i -gt $j ];do
echo -n -e "${i}X${j}=$[$i*$j]\t"
let i++
done
echo
let i=1
let j++
done
循环控制语句(用于循环体中)
continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断
while CONDTITION1;do
CMD1
...
if CONDITION;then
continue
fi
CMDn
...
done
break [N]:提前结束循环
break [N] :提前结束第N 层循环,
while CONDTITION1;do
CMD1
...
if CONDITION;then
break
fi
CMDn
...
done
示例
求100以内所有偶数之和,要求循环遍历100以内的所有正整数
#!/bin/bash
# filename even.sh
# author:danran
# time is 2017-06-22
declare -i i=0
declare -i sum=0
until [ $i -gt 100 ];do
let i++
if [ $[i%2] -eq 1 ];then
continue
fi
let sum+=$i
done
echo "Even sum:$sum"
创建死循环
while true;do
循环体
done
until false;do
循环体
done
示例
每隔3秒钟到系统上获取已经登录的用户的信息:如果docker登录了,则记录于日志中,并退出
read -p "Enter a user name: " username
while true;do
if who | grep "^$username" &> /dev/null;then
break
fi
sleep 3
done
echo "$username logged on" >> /tmp/user.log
法二
#!/bin/bash
#
read -p "Enter a user name: " username
until who | grep "^$username" &> /dev/null;do
sleep 3
done
echo "$username logged on" >> /tmp/user.log
练习
写一个脚本:完成如下任务:
(1)显示一个菜单
cpu)show cpu information:
mem)show memory information
disk)show disk information quit)quit
(2)提示用户选择选项
(3)显示用户选择的内容
进一步的:
用户选择,并显示完成后不退出脚本;而是提示用户继续选择显示其内容:直到使用quit方始退出;
法一
#!/bin/bash
# filename sysinfo.sh
# author:danran
# time is 2017-06-22
cat << EOF
cpu) show cpu information
mem) show memory information
disk)show disk information
quit)quit
============================
EOF
read -p "Enter a option: " option
while [ "$option" != 'cpu' -a "$option" != 'mem' -a "$option" != 'disk' -a "$option" != 'quit' ];do
read -p "Wrong option.Enter again: " option
done
if [ "$option" == 'cpu' ];then
lscpu
elif [ "$option" == 'mem' ];then
cat /proc/meminfo
elif [ "$option" == 'disk' ];then
fdisk -l
else
echo "Quit"
exit 0
fi
条件判断:case语句
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
默认分支
;;
easc
- case 支持glob 风格的通配符:
*:任意长度任意字符
?:任意单个字符
[]:指定范围内的任意单个字符
a|b:a或b
法二
#!/bin/bash
# filename sysinfo.sh
# author:danran
# time is 2017-06-22
cat << EOF
cpu) show cpu information
mem) show memory information
disk)show disk information
quit)quit
============================
EOF
read -p "Enter a option: " option
while [ "$option" != 'cpu' -a "$option" != 'mem' -a "$option" != 'disk' -a "$option" != 'quit' ];do
read -p "Wrong option.Enter again: " option
done
case "$option" in
cpu)
lscpu
;;
mem)
cat /proc/meminfo
;;
disk)
fdisk -l
;;
*)
echo "Quit.."
exit 0
;;
esac
练习:写一个脚本,完成如下要求
(1)脚本可接受参数,start,stop,restart,status
(2)如果参数非此四者之一,提示使用格式后报错退出
(3)如果是start,则创建/var/lock/subsys/SCRIOT_NAME,并显示“启动成功”
考虑:如果事先已经启动过一次,该如何处理
(4)如果是stop:则删除/vat/lock/subsys/SCRIPT_NAME,并显示“停止完成”
考虑:如果事先已经停止过了;g该如何处理
(5)如果是restart,则先stop,再start;
考虑:如果本来没有start,再如何处理
(6)如果是status,则
如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示“SCRIPT_NAME is running...”;
如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“SCRIPT_NAME is stopped...”;
其中:SCRIPT_NAME为当前脚本名
循环控制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 循环的特殊用法(遍历文件的每一行):
while read line; do
循环体
done < /PATH/FROM/SOMEFILE
- 依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将行赋值给变量line
- 练习
扫描/etc/passwd文件每一行,如发现GECOS 字段为空,则填充用户名和单位电话为62985600,并提示该用户的GECOS信息修改成功。 - 示例:
#!/bin/bash
while read passwdline
do
uid=`echo $passwdline | cut -d: -f3`
user=`echo $passwdline | cut -d: -f1`
[ "uid" -ge 1000 ] && echo "$user is common user" || echo "$user is system user"
done < /etc/passwd
unset passwdline uid user
- 双小括号方法,即((…))格式,也可以用于算术运算
- 双小括号方法也可以使bash Shell 实现C 语言风格的变量操作
I=10
((I++))
- for循环的特殊格式:
for (( 控制变量初始化; 条件判断表达式; 控制变量的修正表达式))
do
循环体
done
- 控制变量初始化:仅在运行到循环代码段时执行一次
- 控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断
- 示例:求100以内的所有正奇数之和
#!/bin/bash
sum=0
for i in {1..100..2};do
let sum+=i
done
echo sum=$sum
#!/bin/bash
for ((sum=0,i=1;i<=100;i+=2 ));do
let sum+=i
done
echo sum=$sum
#!/bin/bash
sum=0;
i=1;
while [ $i -le 100 ];do
let sum+=1
let i+=2
done
echo sum=$sum
2 . 求100以内的正整数之和
#!/bin/bash
declare -i sum=0
for ((i=1;i<=100;i++));do
let sum+=$i
done
echo "Sum:$sum"
3 . 打印九九乘法表:
#!/bin/bash
# filename multi.sh
# author:danran
# time is 2017-06-22
for ((j=1;j<=9;j++));do
for((i=1;i<j;i++));do
echo -e -n "$[i]X$[j]=$[$i*$j]\t"
done
echo
done
select循环与菜单
select variable in list
do
循环体命令
done
- select循环主要用于创建菜单,按数字顺序排列的示菜单项将显示在标准错误上,并显示PS3提示符,等待用户输入
- 用户输入菜单列表中的某个数字,执行相应的命令
- 用户输入被保存在内置变量REPLY中
select与case
- select用是个无限循环,因此要记住用 break命令退用出循环,或用exit按命令终止脚本。也可以按ctrl+c退出循环
- select和经常和case联合使用
- 与for循环类似,可以省略inlist,此时使用位置参量
- 示例:创建菜单
#!/bin/bash
PS3="Please choose your menu: "
select menu in work home office exit
do
case $menu in
work)
echo "go work"
;;
home)
echo "go home"
;;
office)
echo "go office"
;;
*)
echo unknow
break
esac
echo your choose is $menu
echo "Your input is $REPLY"
done
信号捕捉trap
- trap ' 触发指令' 信号
自定义进程收到系统发出的指定信号后,将执行触发指令,而不会执行原操作 - trap '' 信号
忽略信号的操作 - trap '-' 信号
恢复原信号的操作 - trap -p
列出自定义信号操作
trap示例
#!/bin/bash
trap 'echo “signal:SIGINT"' int
trap -p \\打印trap自定义命令
for((i=0;i<=10;i++))
do
sleep 1
echo $i
done
trap '' int //int信号设置为空
trap -p \\打印trap自定义命令
for((i=11;i<=20;i++))
do
sleep 1
echo $i
done
trap '-' int //恢复int信号的操作
trap -p \\打印trap自定义命令
for((i=21;i<=30;i++))
do
sleep i
echo $i
done
网友评论