shell中常用的控制语句及脚本运行控制
学习目标:
for 语句while 语句
if 语句
case 语句
expect 语句
exit break continue
exit直接退出当前脚本break仅仅退出本次循环
continue 退出本次循环进行下一次循环
shell脚本中没有return命令
1.for语句:
for NUM in {1..3}
for NUM in `seq 1 3`或者for NUM in `seq 1 2 10`
do
done
1 vim file.sh
#!/bin/bash
for NUM in {1..5}
do
if
[ "$NUM" -eq 3 ]
then
$1
fi
echo $NUM
done
2 sh file.sh 1
3 sh file.sh
4 sh file.sh exit #直接退出当前脚本,当NUM=3时,直接退出
5 sh file.sh break #仅仅退出本次循环,还会执行for循环之后的内容
6 sh file.sh continue #退出本次循环进行下一次循环
7 sh file.sh return #没有该命令
8 vim file.sh #return
#!/bin/bash
ECHO()
{
echo test return
}
for NUM in {1..5}
do
if
[ "$NUM" -eq 3 ]
then
return ECHO
fi
echo $NUM
done
echo hello westos !!!
9 sh file.sh
1
2
file.sh: line 11: return: ECHO: numeric argument required
file.sh: line 11: return: can only `return' from a function or sourced script
3
4
5
hello westos !!!
1.1seq 作用:
`seq 1 5`可以设置步长,可以有变量{1..5}不可以设置步长。for语句参数可以传到循环以外,是嵌套关系,总共执行m*n次
for语句的变量仅仅最后变量被使用,循环定义变量依次执行,进行批处理。
1 vim for.sh
#!/bin/bash
for NUM in {a..f}
do
echo $NUM
done
sh for.sh #从a-f
2 vim for.sh
#!/bin/bash
for NUM in `seq 1 3 8`
do
echo $NUM
done
sh for.sh #步长为3,输出1 4 7
1.2>FOR循环
vim for.sh
#!/bin/bash
for A in {1..5}
do
for NUM in {1..5}
do
echo $A
done
done
echo $A
sh for.sh #1-5各循环5次
脚本实验:
检测教室能上网的机子:
1 vim check_host.sh
2 sh check_host.sh
3 cat check_host.sh
#!/bin/bash
for ((A=1;A<=50;A++))
do
ping -c1 -w1 172.25.254.$A &>/dev/null && echo 172.25.254.$A
done
脚本实验:
对数据库做备份,每个数据库备份一个文件,例如mysql.sql将文件存储到/mnt/mysql_backup
建立两个数据库两个数据表插入数据进行备份操作:1 systemctl start mariadb
2 mysql
3 mysql -uroot -EN -e "show databases;" | grep -E "^\*|schema$" -v #过滤出需要备份的库
4 vim mysql_backup.sh
#!/bin/bash
DATABASE_MESSAGE=`mysql -uroot -EN -e "show databases;"|grep -E "^\*|schema$" -v`
mkdir -p /mnt/mysql_dump
for DATABASE_NAME in $DATABASE_MESSAGE #备份次数
do
mysqldump -uroot $DATABASE_NAME > /mnt/mysql_dump/${DATABASE_NAME}.sql
[ "$?" -eq "0" ] && { #根据退出值判断备份是否完成
echo -e "\033[32m$DATABASE_NAME is backuped !!\033[0m"
}
done
5 sh mysql_backup.sh
6 ls /mnt/mysql_dump/
2 while语句:
while true 条件为真就执行do
done
脚本实验:
编写脚本当负载超过80%时发送邮件给超级用户:
1 df -h | awk '/\/$/{print $5}' | awk -F "%" '{print $1}' #过滤出所占内存的数字
2 echo load average | mail -s warning root
3 mail
4 vim mail_warning.sh
#!/bin/bash
DISK_NUM=`df -h | awk '/\/$/{print $5}' | awk -F "%" '{print $1}'`
#TTY=`ps $$|awk '/bash$/{print $2}'`
while true
do
[ "$DISK_NUM" -ge "80" ]&&{
echo "Your / will full !!" |mail -s warning root
}
sleep 1
done
5 df
6 dd if=/dev/zero of=/bigfile bs=1M count=5000 #测试当/内存超过80%,是否会发送邮件给root
7 sh mail_warning.sh
8 ls /var/spool/mail
9 cat /var/spool/mail/root
3.if语句
3.1>语句示例:
if [ "$1"=="start" ]
then
systemctl start $2
elif [ "$1"=="stop" ]
then
systemctl stop $2
else
echo "error:please input start or stop after scripts !!"
fi
3.2>固定格式:
if
then
elif
then
else
fi
写一个脚本,检测文件是否存在,如果存在,并判断文件类型
方法一:常规方法
1 vim file_check.sh
#!/bin/bash
if
[ "$#" -ne 1 ]
then
echo ERROR
elif
[ -L "$1" ]
then
echo $1 is a link
elif
[ -S "$1" ]
then
echo $1 is a socket
elif
[ -b "$1" ]
then
echo $1 is a block
elif
[ -c "$1" ]
then
echo $1 is a common
else
echo unknown $1
fi
2 sh check_file.sh FILENAME
方法二:vim Check_file.sh #用函数实现文件类型的检测
#!/bin/bash
Check_file
{
if
[ "$1" "$2" ] #$1为Check_file函数之后的$1为判断文件类型判断,$2为文件名称,即脚本后的第一串字符
then
echo "$2" is $3
exit 0
fi
}
if
[ "$#" -ne "1" ]
then
echo "Please input a file follow script!!"
elif
[ ! -e "$1" ]
then
echo "$1 is not exist !!"
else
Check_file -S $1 socket
Check_file -b $1 block
Check_file -c $1 character
Check_file -L $1 link
Check_file -d $1 directory
fi
测试结果:
编写一个脚本:建立用户当出现以下情况报错:
1 文件数量不对报错
2 文件不存在
3 文件行数差异
4 用户存在显示用户存在,但不改变此用户密码
5 当用户不存在建立用户并设定相应密码
实验思路:
1.规则判定:Check Rule
2.动作执行:Create User
vim user_create.sh
#!/bin/bash
################## Check Rule ##################
if
[ "$#" -ne "2" ] #脚本后面有两串字符
then
echo "ERROR1:Please input userfile and passfile follow the script!!"
exit 1
elif
[ ! -e "$1" ] #检测文件是否存在
then
echo "ERROR2:Sorry, $1 is not exist !!"
exit 1
elif
[ ! -e "$2" ]
then
echo "ERROR3:Sorry, $2 is not exist !!"
exit 1
elif
USERFILE_LINE=`awk 'BEGIN{N=0}{N++}END{print N}' $1`
PASSFILE_LINE=`awk 'BEGIN{N=0}{N++}END{print N}' $2`
[ "$USERFILE_LINE" -eq "$PASSFILE_LINE" ] #检测文件行数是否对应,避免有用户没有密码
then
echo "ERROR4:$1's line is different from $2's line !!"
exit 1
fi
################CREATE USER################
for LINE_NUM in `seq 1 $USERFILE_LINE` #for循环取文件中的用户名
do
USERNAME=`sed -n ${LINE_NUM}p $1`
PASSWORD=`sed -n ${LINE_NUM}p $2`
useradd $USERNAME &&{ #检测建立的用户是否已经存在,当没有报错时,将密码给新建用户
echo $PASSWORD | passwd --stdin $USERNAME
}
done
实验操作步骤:
1 vim user_create.sh
2 sh user_create.sh #测试
3 sh user_create.sh userfile passfile
4 touch userfile
5 sh user_create.sh userfile passfile
6 touch passfile
7 sh user_create.sh userfile passfile
8 vim userfile
9 sh user_create.sh userfile passfile
10 vim passfile
11 sh user_create.sh userfile passfile
12 id user1
13 userdel -r user1
14 sh user_create.sh userfile passfile
15 id user1
4.case语句:
case 同步字符匹配
4.1>case结构:
case
word1)
action1
;;
word2)
action2
;;
......
*)
action_last
esac
4.2>case语句示例:
case $1 in
westos)
echo linux
;;
linux)
echo westos
;;
*)
echo "Error: input westos or linux after script !!"
esac
4.3 >脚本实验:
写一个脚本,当$1是cat,输出为dog,当$1 是dog,输出为cat如果是其他的,输出为"please input cat or dog foolow scripts !!"
方法一:if语句
1 >vim test.sh
#!/bin/bash
if
[ "$1" = dog ]
then
echo cat
elif
[ "$1" = cat ]
then
echo dog
else
echo "please input cat or dog follow script !!"
fi
上面的脚本语句有前后顺序,影响运行效率
测试:
2 sh -x test.sh dog
3 sh -x test.sh cat
4 sh -x test.sh
方法二:
case语句
1 vim case.sh
#!/bin/bash
case $1 in
dog)
echo cat
;;
cat)
echo dog
;;
*)
echo error
esac
2 sh -x case.sh dog
3 sh -x case.sh cat
4 sh -x case.sh
结果对比:
用非交互式删除分区:
1 fdisk -l
2 vim delete_partiton.sh
#!/bin/bash
fdisk /dev/vdb <<EOF
d
2
wq
EOF
partprobe
3 sh delete_partiton.sh
4 fdisk -l
用非交互式建立分区:$1为分区内存:
1 vim create_partiton.sh
#!/bin/bash
fdisk /dev/vdb <<EOF
n
+$1
wq
EOF
partprobe
2 sh create_partiton.sh 1G
3 fdisk -l
很明显上面的方法是有bug的,如果建立新分区时有扩展分区,但是还想建主分区,可是拓展分区占用了剩余所有的内存,是不是就会报错,此时我们就需要考虑建分区时,是否已经建立了拓展分区,如果建立了怎么办,如果没有建立怎么办?如果建立分区时已经有了拓展分区怎么办?:
vim create_partiton1.sh
#!/bin/bash
DESK_MESSAGE=`fdisk -l |awk '/Extended/'`
if
[ -z "$DESK_MESSAGE" ]
then
fdisk /dev/vdb <<EOF
n
e
wq
EOF
partprobe
fi
5 expect
expect是自动应答命令用于交互式命令的自动执行expect是一种自动交互语言,能实现在shell脚本中为scp和ssh等自动输入密码自动登录。spawn是expect中的监控程序,其运行后台会监控命令提出的交互问题
send 发送问题答案给交互命令
"\r" 表示回车
exp_continue 表示当问题不存在时继续回答下面的问题
expect eof 表示问题回答完毕退出expect环境
interact 表示问题回答完毕退出expect环境;//交互模式,用户会永远停在远程服务器上面
set NAME [lindex $argv n] 定义变量
命令在:
/usr/bin/expect
实验脚本:
1 vim ask.sh #自问自答
#!/bin/bash
read -p "What's your name: " NAME
read -p "How old are you: " AGE
read -p "Where are you from: " CITY
read -p "Are you happy: " FEEL
echo "$NAME is $AGE and come from $CITY ; She feel $FEEL "
2 sh ask.sh
3 yum install expect -y
4 vim answer.exp 自动应答命令用于交互式命令的自动执行
#!/usr/bin/expect
set timeout 2 //设置超时时间
spawn /mnt/ask.sh //
expect "name:" //接收参数
send "liujiayi\r"
expect "old:"
send "18\r"
expect "from:"
send "Zhouzhi\r"
expect "happy:"
send "happy\r"
expect eof
4 chmod +x /mnt/ask.sh
5 expect answer.exp
1 vim answer1.exp
#!/usr/bin/expect
set timeout 2
spawn /mnt/ask.sh
expect { //返回信息匹配
name { send "liujiayi\r";exp_continue }
old { send "18\r";exp_continue }
from { send "Zhouzhi\r";exp_continue }
happy { send "happy\r" }
}
expect eof
2 expect answer1.sh
3 vim answer2.exp
#!/usr/bin/expect
set timeout 2
set NAME [lindex $argv 0] #在expect中表示exp文件后的第一串字符,和shell中的$1相似
set AGE [lindex $argv 1]
set CITY [lindex $argv 2]
set FEEL [lindex $argv 3]
spawn /mnt/ask.sh
expect {
name { send "$NAME\r";exp_continue }
old { send "$AGE\r";exp_continue }
from { send "$CITY\r";exp_continue }
happy { send "$FEEL\r" }
}
expect eof
4 expect answer2.exp
1 vim answer3.sh #脚本调用expect中的命令,实现非交互式应答
#!/bin/bash
/usr/bin/expect <<EOF
set timeout 2
spawn /mnt/ask.sh
expect {
name { send "$1\r";exp_continue }
old { send "$2\r";exp_continue }
from { send "$3\r";exp_continue }
happy { send "$4\r" }
}
expect eof
EOF
2 sh answer3.sh
3 sh answer3.sh lee 18 Dongbei happy!
连接教室里面1-10主机,并显示他们的主机名
1 vim auto_connect.sh
#!/bin/bash
for NUM in {1..10}
do
ping -c1 -w1 172.25.254.$NUM &>/dev/null && {
/usr/bin/expect <<EOF
set timeout 3
spawn ssh [email protected].$NUM "hostname"
expect {
"yes/no" { send "yes\r";exp_continue }
"password" { send "$2\r" }
}
expect eof
EOF
}
done
连接教室里面1-10主机,并显示他们的主机名,并将他们的主机名定向到/mnt/hostame中:
1 vim auto_connect2.sh
#!/bin/bash
AUTO_CONNECT()
{
/usr/bin/expect <<EOF
set timeout 10
spawn ssh [email protected].$IP hostname
expect {
"yes/no" { send "yes\r";exp_continue }
"password" { send "westos\r" }
}
expect eof
EOF
}
for IP in {64..65}
do
ping -c1 -w1 172.25.254.$IP &>/dev/null && {
NAME=`AUTO_CONNECT|grep -E "authenticity|ECDSA|connecting|Warning|password|spawn" -v`
}
echo $NAME 172.25.254.$IP | sed 's/\r//g'>>/mnt/hostname
done
2 sh auto_connect2.sh
3 ls #测试,看是否生成了hostname文件,并查看文件内容:
4 cat hostname
ctrl+v ^
ctrl+m M
^M表回车
vim auto_connecton.sh
\n\r 换行+回车
unix中\r表换行
linux中\n表换行,\r回车