shell基础(四)
一:while循环
while <条件表达式> #此处可以是(())、[]、[[]]和前面条件表达式判断一样
do
指令..
done
while循环主要是
1.重复执行一组命令,常常用于守护进程或无限循环的程序(要加sleep和usleep控制频率)。
2.适用于频率小于1分钟的循环处理,其他的都可以用crond功能代替
注:
sleep 1 # 休息一秒
usleep 1000000 # 休息1000000微秒,即休息一秒
例一:
#!/bin/bash
while [ 1 ] #[ 1 ]和true 一样,只是前面2边需要有空格
do
uptime >>/tmp/uptime.log
sleep 2
done
#uptime用于查看系统负载,具体可参见http://os.51cto.com/art/201312/420840.htm
例二:计算1+2...100的和
方式一:
#!/bib/sh
SUM=0
I=1
while [ $I -le 100 ] #[]中需要变量取值符号"$",而且大于小于只能用"-le"
do
((SUM+=I))
let I++
done
echo $SUM
-------------------------------------------------------------------------------------------------
方式二:
#!/bib/sh
SUM=0
I=1
while ((I<=100)) #(())中可以不要变量取值符号,大于小于是可以直接用
do
((SUM+=I))
((I++))
done
echo $SUM
-------------------------------------------------------------------------------------------------
方式三:
#!/bib/sh
SUM=0
I=100
((SUM=I*(I+1)/2))
echo $SUM
实战一
手机充值10元,每发一次短信花费1角5分,当余额低于1角5分时就不能在发短信,并且提示"余额不足,请充值",用while实现
简单版一
#!/bin/bash
sum=1000
fee=15
while [ $sum -gt $fee ]
do
echo "send message successfully"
((sum-=fee))
echo "left $sum"
done
echo "余额不足,请充值"
--------------------------------------------------------------------------------------------
专业版二:
#!/bin/bash
export LANG="zh_CN.UTF-8" #定义中文字符防止乱码
sum=15 #总钱数
msg_fee=15 #每条短信的钱数
function menu(){
cat <<END
当前余额${sum},每条短信需要${msg_fee}
==============
1.充值余额
2.发送短信
3.退出
==============
END
}
#充钱
function recharge(){
read -p "请输入充值金额:" money
expr $money + 1 &>/dev/null
if [ $? -ne 0 ];then
echo "must be int"
else
((sum+=money))
echo "您的余额为$sum"
fi
}
#发信息
function sendInfo(){
if ((sum < nsg_fee));then #先判断钱够不够
echo "您的余额不足,请充值"
else
while true
do
read -p "请输入内容(不要带空格):" message
echo "$message 已发送"
((sum-=msg_fee))
return 0 #此处发送完成后,要去菜单选择,而不是一直发短信
if [ $sum -lt $msg_fee ];then
printf "温馨提示:您的余额不足"
return 1
fi
done
fi
}
function main(){
while true #一直显示菜单,若想退出数字"3",即可退出
do
menu
read -p "请输入一个数字:" NUM
case "$NUM" in
1)
recharge
;;
2)
sendInfo
;;
3)
exit 0
;;
*)
echo "must be {1|2|3}"
esac
done
}
main
效果如图所示:
[centos@mycentos iphone_fee]$ sh 2.sh
当前余额15,每条短信需要15
==============
1.充值余额
2.发送短信
3.退出
==============
请输入一个数字:1
请输入充值金额:100
您的余额为115
当前余额115,每条短信需要15
==============
1.充值余额
2.发送短信
3.退出
==============
请输入一个数字:2
请输入内容(不要带空格):hello
hello 已发送
当前余额100,每条短信需要15
==============
1.充值余额
2.发送短信
3.退出
==============
请输入一个数字:3
[centos@mycentos iphone_fee]$
注:case常用于服务启动脚本中,常用cat的here文档的方式打印菜单
实战二
用while守护进程的方式去监控网站,每10秒确定一次是否正常,若不正常,就发邮箱通知
预备知识:
curl -I www.qq.com : 显示响应头部信息
curl -o 1.txt www.qq.com : 将网页下载到1.txt文件中
curl命令解释:
curl -o /dev/null -s -w %{http_code} http://zys.8800.org/
-o 参数,是把下载的所有内容都重定向到/dev/null,-s命令,是屏蔽了curl本身的输出,而-w参数,是根据我们自己的需要,自定义了curl的输出格式。
用上面的命令采集页面的状态码,返回200表示页面正常,其他返回码则是异常。
代码如下:
#!/bin/bash
if [ $# -ne 1 ];then
echo $"usage:$0 url"
exit 1
fi
while true #守护进程
do
#取回页面的状态码
if [ $( curl -o /dev/null --connect-timeout 3 -s -w "%{http_code}" $1 | egrep -w "200|301|302" | wc -l) -ne 1 ];then
echo "$1 is error"|mail -s "$1 is error" 111111@qq.com
else
echo "$1 is ok"
fi
sleep 10
done
知识点补充:
1、当运行脚本时,若突然中断,优肯会导致数据的丢失,则需要防止中断的方式
1.使用"sh shell.sh &"命令 ,即使用&在后台运行
2.使用"nohup shell.sh &"命令,即使用nohup 加&在后台运行脚本
3.利用screen保持会话,然后再执行脚本,即使用screen保持当前会话
后台运行的知识:
sh shell.sh & 脚本shell.sh 放在后台执行
Ctrl+c 停止执行当前脚本或任务
Ctrl+z 暂停执行当前脚本
jobs 查看当前执行的脚本或任务
bg 将当前脚本或任务放到后台执行
kill 关闭当前脚本任务,即以"kill %号码"的形式关闭进程,任务号通过jobs获得
fg 将当前的脚本或任务放到前台执行,若有多任务,则"fg 号码"调出相应的任务编号
效果如图:
[centos@mycentos shell]$ sh 2.sh & #后台运行
[1] 2988
[centos@mycentos shell]$ jobs #查看任务数
[1]+ Running sh 2.sh &
[centos@mycentos shell]$ kill %1 #关闭进程为1的进程
[centos@mycentos shell]$ jobs
[1]+ Terminated sh 2.sh #程序已经被关闭
2.while按行读取文件的几种方式
1).
exec <FILE
sum=0
while read line
do
cmd
done
---------------------------------------------------------------------------------------------------
2).
cat file | while read line
do
cmd
done
-------------------------------------------------------------------------------------------------
3).while read line
do
cmd
done<file
例如:开发一个shell脚本实现cat读文件的基本功能
#!/bin/bash
while read line
do
echo $line
done<$1
二:for循环
for 变量名 in 变量取值列表
do
指令
done
注:读取取值列表时默认以空格分割
C语言版循环
for ((exp1;exp2;exp3))
do
指令。。。
done
例如:
for ((i=0;i<=3;i++))
do
echo $i
done
注:"in 变量取值列表" 可以省略,省略时相当于in "$@",即for i 就等于 for i in "$@"
例一:打印5、4、3、2、1这五个数字
方式一:
#!/bin/bash
#直接列出变量列表,打印5、4、3、2、1 以空格为分隔符
for NUM in 5 4 3 2 1
do
echo $NUM
done
-------------------------------------------------------------------------------------------------
方式二:
#!/bin/sh
#用{}号实现
for NUM in {5..1}
do
echo $NUM
done
-------------------------------------------------------------------------------------------------
方式三:
#!/bin/bash
#5是起始数字 -1是步长,即每次减一 1是结束数字
for NUM in $(seq 5 -1 1)
do
echo $NUM
done
实战三:批量更改文件名,将目录下以".txt"结尾的全部变成".gif"
思路:先处理一个,再批量处理
[centos@mycentos test]$ ll
total 0
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 1.txt
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 2.txt
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 3.txt
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 4.txt
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 5.txt
1.先处理一个:
[centos@mycentos test]$ file=1.txt
[centos@mycentos test]$ echo $file
1.txt
[centos@mycentos test]$ echo $file | cut -d '.' -f1
1
[centos@mycentos test]$ echo $(echo $file | cut -d '.' -f1).gif #得到要变成的样子
1.gif
[centos@mycentos test]$ mv $file $(echo $file | cut -d '.' -f1).gif
[centos@mycentos test]$ ll
total 0
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 1.gif
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 2.txt
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 3.txt
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 4.txt
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 5.txt
2.脚本批量处理:
#!/bin/bash
for file in $(ls | grep "txt$")
do
mv ${file} $(echo $file | cut -d . -f1).gif
done
结果:
total 0
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 1.gif
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 2.gif
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 3.gif
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 4.gif
-rw-rw-r--. 1 centos centos 0 Nov 13 13:58 5.gif
实战四:
在linux下批量修改文件名,将下列文件名中"_finished"去掉
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_1_finished.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_2_finished.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_3_finished.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_4_finished.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_5_finished.jpg
方法一:
先处理一个后批量处理
#!/bin/sh
for list in $(ls *.jpg)
do
mv $list $(echo $list |sed 's/_finished//g')
done
---------------------------------------------------------------------------------------------
方法二:(awk)
[centos@mycentos old]$ ls | awk -F '.' '{print $0,$1,$2}' #$0为本身
ch.sh ch sh
sku_102999_1.jpg sku_102999_1 jpg
sku_102999_2.jpg sku_102999_2 jpg
sku_102999_3.jpg sku_102999_3 jpg
sku_102999_4.jpg sku_102999_4 jpg
sku_102999_5.jpg sku_102999_5 jpg
[centos@mycentos old]$ ls | awk -F '.' '{print $0,$1"_finished."$2}' #进行拼接
ch.sh ch_finished.sh
sku_102999_1.jpg sku_102999_1_finished.jpg
sku_102999_2.jpg sku_102999_2_finished.jpg
sku_102999_3.jpg sku_102999_3_finished.jpg
sku_102999_4.jpg sku_102999_4_finished.jpg
sku_102999_5.jpg sku_102999_5_finished.jpg
[centos@mycentos old]$ ls | awk -F '.' '{print "mv", $0,$1"_finished."$2}'
mv ch.sh ch_finished.sh
mv sku_102999_1.jpg sku_102999_1_finished.jpg
mv sku_102999_2.jpg sku_102999_2_finished.jpg
mv sku_102999_3.jpg sku_102999_3_finished.jpg
mv sku_102999_4.jpg sku_102999_4_finished.jpg
mv sku_102999_5.jpg sku_102999_5_finished.jpg
[centos@mycentos old]$ ls | awk -F '.' '{print "mv", $0,$1"_finished."$2}' | bash #将字符交给bash处理
[centos@mycentos old]$ ll
total 4
-rw-rw-r--. 1 centos centos 93 Nov 13 14:37 ch_finished.sh
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_1_finished.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_2_finished.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_3_finished.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_4_finished.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_5_finished.jpg
-------------------------------------------------------------------------------------------------
方法三:
[centos@mycentos old]$ rename "_finished" "" *jpg #不要忘了"" ,空格代表去除
[centos@mycentos old]$ ll
total 4
-rw-rw-r--. 1 centos centos 93 Nov 13 14:37 ch_finished.sh
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_1.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_2.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_3.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_4.jpg
-rw-rw-r--. 1 centos centos 0 Nov 13 14:31 sku_102999_5.jpg
rename 例子补充:
批量去掉文件名中的"bd"
[centos@mycentos db]$ ll
total 0
-rw-rw-r--. 1 centos centos 0 Nov 13 14:51 bd02.html
-rw-rw-r--. 1 centos centos 0 Nov 13 14:51 bd03.html
-rw-rw-r--. 1 centos centos 0 Nov 13 14:51 bd04.html
-rw-rw-r--. 1 centos centos 0 Nov 13 14:51 bd05.html
rename方式:
[centos@mycentos db]$ rename "bd" "" *.html
[centos@mycentos db]$ ll
total 0
-rw-rw-r--. 1 centos centos 0 Nov 13 14:51 02.html
-rw-rw-r--. 1 centos centos 0 Nov 13 14:51 03.html
-rw-rw-r--. 1 centos centos 0 Nov 13 14:51 04.html
-rw-rw-r--. 1 centos centos 0 Nov 13 14:51 05.html
注:
先去掉一个,后批量去除也可
实战五:通过脚本实现仅仅sshd,network,crond,sysstat,rsyslog服务在开机时自启动,chkconfig --list是查看所有服务开机时的情况。
方式一:
先统一关闭,后开启特定的
for old in $(chkconfig --list | grep "3:on" | awk '{print $1}' )
do
chkconfig --level 3 $old off
done
for old in crond network rsyslog sysstat sshd
do
chkconfig --level 3 $old on
done
-------------------------------------------------------------------------------------------------
方式二:
因为默认情况下开机需要保留的服务都是开启,所以将其他的关闭就好
for old in $(chkconfig --list | grep "3:on" |awk '{print $1}' egrep -v "crond|network|rsyslog|sshd|sysstat")
do
chkconfig --level 3 $old off
done
-------------------------------------------------------------------------------------------------
方式三:
awk的拼接
chkconfig --list | egrep -v " sshd|crond|rsyslog|sysstat|network" |awk '{print "chkconfig", $1, "off"}' |bash
效果如图:
[root@mycentos ~]# chkconfig --list | egrep -v "
sshd|crond|rsyslog|sysstat|network" |awk '{print "chkconfig", $1, "off"}' #awk中的","变成显示后的空格进行分割
chkconfig wdaemon off
chkconfig winbind off
chkconfig wpa_supplicant off
chkconfig xinetd off
chkconfig ypbind off
.....
chkconfig --list | egrep -v " sshd|crond|rsyslog|sysstat|network" |awk '{print "chkconfig", $1, "off"}' | bash #将上面的当做命令传输到bash运行
可以简化成:
chkconfig | egrep -v " sshd|crond|rsyslog|sysstat|network" |awk '{print "chkconfig", $1, "off"}' | bash
实战六:打印九九乘法表
#!/bin/sh
COLOR='\E[47;30m'
RES='\e[0m'
for line in $(seq 9)
do
for col in $(seq ${line})
do
if (((line*col)>9));then #此处用来控制输出格式,当是大于9时后面是一个空格,小于等于9时就是2个空格
echo -en "${COLOR}${col}*${line}=$((line*col))${RES} " #一个空格 -e用来显示文字,-n为了不换行
else
echo -en "${COLOR}${col}*${line}=$((line*col))${RES} " #2个空格
fi
done
echo ' ' #一行完成后换行
done
效果如图:
未标题-1.png
实战七:
批量创建10个系统账号(oldboy01-oldboy10),并且设置密码(密码是随机数,要求是字符和数字的混合)
思路:
1.创建账号
seq -w 10:表示 01-10 :格式对齐
或者
{01..10}
2.创建账户,设置无交互密码
useradd oldboy
echo 111111 | passwd --stdin oldboy
3.随机八位密码(字母数字混合)
小插曲:
生成随机数的方式
1.利用RANDOM(范围是0-32767)随机生成数字,然后利用md5进行加密,取5到12为字符
[root@mycentos ~]# echo $RANDOM |md5sum | cut -c 5-12
1684ebbf
若只有RANDOM,则安全性不高,$RANDOM前面加上自己定制的秘钥,安全就会增加
echo "oldboy$RANDOM" 此时的就无法破解
2.利用OpenSSL生成随机数
[root@mycentos shell]# openssl rand -base64 8
FAQco/00NL0=
[root@mycentos shell]# openssl rand -base64 80
s0KqTLEA6fIueDcsJkPaqzd3owXNVUcN+d+9FyiMn1sXYShjmsDgbzrndvrn9o8i
ZbjkSZzahr1ZMMbLcqJ/DvCfMAhEcZyG/hIvKMzkr8c=
3.利用/dev/urandom ,这里记录系统此时状态,转换成数字
[root@mycentos shell]# head /dev/urandom | cksum
434988922 2930
[root@mycentos shell]# head /dev/urandom | cksum
3283962471 2161
4.通过日期生成随机数;
[root@mycentos shell]# date +%s%N
1542160944683910406
以上的随机数长短不一,通过md5sum进行统一格式
1.echo "test$RANDOM" |md5sum|cut -c 2-9
2.openssl rand -base64 80 | md5sum|cut -c 2-9
3.date +%s%N |md5sum|cut -c 2-9
4.head /dev/urandom | md5sum |cut -c 2-9
方式一:
#!/bin/bash
user="oldboy"
passfile="/tmp/user1.log"
for num in $(seq -w 10)
do
useradd $user$num #创建用户
pass="$(echo "test$RANDOM" |md5sum|cut -c 3-11)" #先用秘钥加密后取得密码,用变量保存
echo "$pass"|passwd --stdin $user$num &>/dev/null #将密码赋值给用户
echo -e "user:$user$num\tpasswd:$pass">>$passfile #将用户信息放到固定文件中,-e参数处理特殊字符,此处将"\t"处理成tab效果,而不
是直接输出"\t"
done
echo "------------------------------------------------------"
cat $passfile
特别注意:RANDOM是一个随机数,每次都不一样,因此如果需要用到2次,就一定要先用变量保存
------------------------------------------------------------------------------------------------
方式二:
用chpasswd批量创建密码
批量创建密码:用命令chpasswd
[root@mycentos shell]# useradd old
[root@mycentos shell]# echo "old:123456"|chpasswd
[root@mycentos shell]# su - oldgirl #root切换成普通用户不需要密码
[oldgirl@mycentos ~]$ su - old
Password:
[old@mycentos ~]$ whoami
old
给多个用户设置密码:
chpasswd < 密码文件
其中密码文件格式:
用户名1:口令1
用户名2:口令2
前提:用户必须存在
#!/bin/bash
. /etc/init.d/functions
user="xioaming"
passfile="/tmp/user.log"
for num in $(seq -w 10) #批量创建用户和密码
do
pass="$(echo "test$RANDOM" |md5sum|cut -c 3-11)"
useradd ${user}${num} &>/dev/null && \
echo -e "${user}${num}:$pass" >> $passfile
if [ $? -eq 0 ];then
action "${user}${num} is ok" /bin/true
else
action "${user}${num} is fail" /bin/false
fi
done
echo "-------------------------------------------------"
chpasswd < $passfile #chpasswdd 用于批量给用户设置密码,此处将上面批量设置的用户和密码一一对应
cat$passfile && > $passfile
实战八:
打印选择菜单,按照选项意见按照不同的web服务
[root@mycentos select]# sh menu.sh
1.[install lamp]
2.[install lanp]
3.exit
input the num you want:工作中所用的lamp
要求:
1.当用户输入1时,输出"start installing lamp"提示,然后执行/server/scripts/lamp.sh输出"lamp is installed",并且退出脚本,此为工作中所用的lamp一键安装脚本
2.当用户输入2时,输出"start installing lnmp" 提示,然后执行/server/scripts/lnmp.sh输出"lnmp is installed",并退出脚本,此为工作中的lnmp一键安装脚本
3.当输入3时,退出当前菜单及脚本提示
4.当输入任何其他字符时,给出"Input error"后退出脚本
5.对执行脚本进行先关的条件判断,例如:脚本文件是否存在,是否可执行等的判断。
[root@mycentos select]# echo "lanp is installed" > /server/scripts/lanp.sh
[root@mycentos select]# echo "lamp is installed" > /server/scripts/lamp.sh
#!/bin/sh
path=/server/scripts #设置脚本文件的路径
[ ! -d "$path" ]&& mkdir -p $path #判断是否存在,不存在就创建
function menu(){ #菜单栏
cat <<END
1.[install lamp]
2.[install lanp]
3.exit
input the num you want:
END
}
function main(){ #主函数
menu #显示菜单
read num #用户选项
expr $num + 1 &> /dev/null # 判断用户输入是否是整数
if [ $? -ne 0 ];then
echo "must input {1|2|3}"
exit 1
fi
case "$num" in
1)
echo "start installing lamp"
sleep 2
if [ -x "$path/lamp.sh" ];then #判断脚本是否可执行
source $path/lamp.sh #加载其他脚本
exit $?
else
echo "$path/lamp.sh does not exist or can not be exec"
exit 1
fi
;;
2)
echo "start installing lanp"
sleep 2
if [ -x "$path/lanp.sh" ];then
source $path/lanp.sh
exit $?
else
echo "$path/lamp.sh does not exist or can not be exec"
exit 1
fi
;;
3)
echo "bye"
exit 3
;;
*)
echo "you must input {1|2|3}"
esac
}
main
注:
1.此处用的是cat用法,还可以用select,但是常用的是cat 的here文档