Linux运维学习笔记之二十八:Shell高级编程
第三十九章 Shell高级编程一、Shell函数1、语法格式(1)简单语法fun_name(){指令。。。return n}(2)规范语法function fun_name(){指令。。。return n}2、函数的执行(1)方法一:直接执行函数名a、格式fun_nameb、注意事项(i)不要带小括号。(ii)函数定义和函
第三十九章 Shell高级编程
一、Shell函数
1、语法格式
(1)简单语法
fun_name(){
指令。。。
return n
}
(2)规范语法
function fun_name(){
指令。。。
return n
}
2、函数的执行
(1)方法一:直接执行函数名
a、格式
fun_name
b、注意事项
(i)不要带小括号。
(ii)函数定义和函数体必须在要执行的函数名的前面,因为shell的执行是从上向下执行的。
(2)方法二:带参数执行
a、格式
fun_name 参数1 参数2 。。。
b、注意事项
(i)在函数体中可以使用位置参数($1,$2..$n,$#,$*,$?,$@)做为函数的参数。
(ii)父脚本的参数则临时被函数的参数所掩盖或变隐藏。
(iii)位置参数$0比较特殊,仍然表示父脚本的名称。
(iv)当函数执行完成后,原来的命令行参数会恢复。
(v)return的功能和工作方式与shell中的exit相同,用于跳出函数。
(vi)return会返回一个退出给调用的程序。
(vii)在函数体中使用exit会跳出shell,终止整个shell脚本。
3、示例
(1)示例1:基本调用的注意事项
a、只定义函数,不调用,则无结果。
vi /services/scripts/myFun01.sh
#!/bin/sh
myFun01(){
echo "I amLinux"
}
sh /services/scripts/myFun01.sh
#<--结果为空,没有任何输出
b、函数调用在前,定义在后,则报错commandnot found。
vi /services/scripts/myFun01.sh
#!/bin/sh
myFun01
myFun01(){
echo "I amLinux"
}
sh /services/scripts/myFun01.sh
myFun01.sh: line 2: myFun01: command not found
c、调用函数加小括号,则报错syntaxerror: unexpected end of file。
vi /services/scripts/myFun01.sh
#!/bin/sh
myFun01(){
echo "I amLinux"
}
myFun01()
sh /services/scripts/myFun01.sh
myFun01.sh: line 6: syntax error: unexpected end of file
d、正确调用。
vi /services/scripts/myFun01.sh
#!/bin/sh
myFun01(){
echo "I amLinux"
}
myFun01
sh /services/scripts/myFun01.sh
I am Linux
(2)示例2:函数定义和函数执行分离于不同脚本
vi /services/scripts/fun_defined.sh
#!/bin/sh
myFun01(){
echo "I ammyFun01"
}
myFun02(){
echo "I ammyFun02"
}
vi /services/scripts/fun_exec.sh
#!/bin/sh
[ -f fun_defined.sh ] && . ./fun_defined.sh || exit 1
myFun01
myFun02
sh fun_exec.sh
I am myFun01
I am myFun02
(3)示例3:函数传参
vi /services/scripts/myFun03.sh
#!/bin/sh
myFun03(){
echo "I am Linux。You are $1"
}
myFun03 abc
sh /services/scripts/myFun03.sh
I am Linux。Youare abc
(4)示例4:函数获取脚本传参
vi /services/scripts/myFun04.sh
#!/bin/sh
myFun04(){
echo "I am myFun04。You are $1"
}
myFun04 $1
sh /services/scripts/myFun04.sh Tom
I am myFun04。Youare Tom
(5)示例5:通过函数传参、脚本传参实现对任意指这URL的异常判断
vi /service/scripts/fun_check_web.sh
#!/bin/sh
httpcode=0
getHttpCode(){
httpcode=`curl -I -s -w "%{http_code}" -o /dev/null $1`
if [ $http_code -ne 200 ]
then
echo " Web is error."
return 1
else
echo " Web isOK."
fi
return 0
}
getHttpCode $1
ehco $?
echo $httpcode
sh fun_check_web.sh http://www.baidu.com
Web is OK.
0
200
sh fun_check_web.sh http://www.baidu.com123
Web is error.
1
000
(6)示例6:开发MySQL单实例或多实例启动脚本
a、思路
(i)MySQL启动命令:
单实例:mysqld_safe --user-mysql &
多实例:mysqld_safe--defaults-file=/data/3306/my.cnf &
(ii)MySQL停止命令
单实例:mysqladmin -uroot -p'123456'shutdown
多实例:mysqladmin -uroot -p'123456' -S/data/3306/mysql.sock shutdown
b、要求
一个start函数调用启动命令,一个stop函数调用停止命令,一个restart函数调用stop函数和start函数。
c、单实例启动脚本
vi /services/scripts/singleDBStart.sh
#!/bin/sh
. /etc/init.d/functions
basedir=/application/mysql/
datadir=/application/mysql/data/
function usage(){
echo "$0{start|stop|restart}"
exit 1
}
[ $# -ne 1 ] && usage
function start_mysql(){
$basedir/bin//mysqld_safe--datadir="$datadir" --user=mysql &
if [ $? -eq 0 ]
then
action "startmysql" /bin/true
else
action "startmysql" /bin/false
fi
}
function stop_mysql(){
mysqladmin -uroot-p'123456' shutdown
if [ $? -eq 0 ]
then
action "stopmysql" /bin/true
else
action "stopmysql" /bin/false
fi
}
if [ "$1" == "start" ]
then
start_mysql
elif [ "$1" == "stop" ]
then
stop_mysql
elif [ "$1" == "restart" ]
then
stop_mysql
start_mysql
else
usage
fi
sh singleDBStart.sh start
start mysql [ OK ]
sh singleDBStart.sh stop
stop mysql [ OK ]
mysql -uroot -p
mysql>
d、多实例启动脚本:查看多实例/data/3306/mysql
cat /data/3306/mysql
#!/bin/sh
#init
port=3306
mysql_user="root"
mysql_pwd="wddgmy"
CmdPath="/application/mysql/bin"
mysql_sock="/data/${port}/mysql.sock"
#startup function
function_start_mysql()
{
if [ ! -e"$mysql_sock" ];then
printf "StartingMySQL...\n"
/bin/sh${CmdPath}/mysqld_safe --defaults-file=/data/${port}/my.cnf 2>&1 >/dev/null &
else
printf "MySQL is running...\n"
exit
fi
}
#stop function
function_stop_mysql()
{
if [ ! -e"$mysql_sock" ];then
printf "MySQL isstopped...\n"
exit
else
printf "StopingMySQL...\n"
${CmdPath}/mysqladmin-u ${mysql_user} -p${mysql_pwd} -S /data/${port}/mysql.sock shutdown
fi
}
#restart function
function_restart_mysql()
{
printf "RestartingMySQL...\n"
function_stop_mysql
sleep 2
function_start_mysql
}
case $1 in
start)
function_start_mysql
;;
stop)
function_stop_mysql
;;
restart)
function_restart_mysql
;;
*)
printf "Usage:/data/${port}/mysql {start|stop|restart}\n"
esac
(7)示例7:将开发MySQL单实例启动脚本通过chkconfig进行管理启动
cp /services/scripts/singleDBStart.sh /etc/init.d/sigleMySQL
vi /etc/init.d/sigleMySQL
#!/bin/sh
# chkconfig: 2345 21 60
# description: sigleMySQL start and mysql stop scripts
. /etc/init.d/functions
basedir=/application/mysql/
datadir=/application/mysql/data/
......
chkconfig --add sigleMySQL
chkconfig sigleMySQL on
chkconfig --list sigleMySQL
sigleMySQL 0:off 1:off 2:on 3:on 4:on 5:on 6:off
二、case结构条件句
1、语法格式
case "字符串变量" in
值1) 指令1...
;;
值2|值3|值4) 指令2...
;;
*) 指令...
esac
2、示例
(1)示例1:根据用户的选择输入,判断是哪种水果并加上不同颜色
a、给字符串加颜色
vi /services/scripts/echo-color01.sh
echo -e "\033[30m BlackFont : this is blackfont \033[0m"
echo -e "\033[31m RedFont : this is redfont \033[0m"
echo -e "\033[32m GreenFont : this is greenfont \033[0m"
echo -e "\033[33m YellowFont : this is yellowfont \033[0m"
echo -e "\033[34m BlueFont : this is bluefont \033[0m"
echo -e "\033[35m PurpleFont : this is purplefont \033[0m"
echo -e "\033[36m SkyBlueFont: this is skybluefont\033[0m"
echo -e "\033[37m WhiteFont : this is whitefont \033[0m"
vi /services/scripts/echo-color02.sh
#!/bin/sh
RED_COLOR='\E[1;31m'
GREEN_COLOR='\E[1;32m'
YELLOW_COLOR='\E[1;33m'
BLUE_COLOR='\E[1;34m'
PINK_COLOR='\E[1;35m'
RES='\E[0m'
echo -e "${RED_COLOR}========red color=========${RES}"
echo -e "${YELLOW_COLOR}======yellow color=========${RES}"
echo -e "${BLUE_COLOR}========blue color=========${RES}"
echo -e "${GREEN_COLOR}=======green color=========${RES}"
echo -e "${PINK_COLOR}========pink color=========${RES}"
b、完整脚本
vi /services/scripts/fruitmenu.sh
#!/bin/sh
RED_COLOR='\E[1;31m'
GREEN_COLOR='\E[1;32m'
YELLOW_COLOR='\E[1;33m'
BLUE_COLOR='\E[1;34m'
RES='\E[0m'
menu(){
cat <<END
==========================
1 apple
2 pear
3 banana
4 exit
=============================
END
}
while true
do
menu
read -p "please ipunt your choice: " -t 10 fruit
case "$fruit" in
1)
echo -e"${RED_COLOR} apple ${RES}"
;;
2)
echo -e"${GREEN_COLOR} pear ${RES}"
;;
3)
echo -e"${YELLOW_COLOR} banana ${RES}"
;;
4)
exit 0
;;
*)
echo "no fruit youchoose"
;;
esac
done
(2)示例2:开发一个给指定内容加指定颜色的脚本
a、要求
(i)使用read或传参实现
(ii)以传参为例,在脚本命令行传2个参数,第1个参数是内容,第2个参数是颜色
b、传参脚本
vi /services/scripts/textcolor.sh
#!/bin/sh
RED_COLOR='\E[1;31m'
GREEN_COLOR='\E[1;32m'
YELLOW_COLOR='\E[1;33m'
BLUE_COLOR='\E[1;34m'
RES='\E[0m'
if [ $# -ne 2 ];then
echo "Usage $0content {red|yellow|blue|green}"
exit
fi
case "$2" in
red|RED)
echo -e"${RED_COLOR} $1 ${RES}"
;;
green|GREEN)
echo -e"${GREEN_COLOR} $1 ${RES}"
;;
yellow|YELLOW)
echo -e"${YELLOW_COLOR} $1 ${RES}"
;;
blue|BLUE)
echo -e"${BLUE_COLOR} $1 ${RES}"
;;
*)
echo "you inputcolor is not exits"
echo "Usage $0content {red|yellow|blue|green}"
;;
esac
sh textcolor.sh apple red
apple
sh textcolor.sh apple green
apple
sh textcolor.sh apple yellow
apple
sh textcolor.sh apple blue
apple
c、函数脚本
vi /services/scripts/textcolor_fun.sh
#!/bin/sh
plus_color(){
RED_COLOR='\E[1;31m'
GREEN_COLOR='\E[1;32m'
YELLOW_COLOR='\E[1;33m'
BLUE_COLOR='\E[1;34m'
RES='\E[0m'
case "$2" in
"red")
echo -e"${RED_COLOR} $1 ${RES}"
;;
"green")
echo -e"${GREEN_COLOR} $1 ${RES}"
;;
"yellow")
echo -e"${YELLOW_COLOR} $1 ${RES}"
;;
"blue")
echo -e"${BLUE_COLOR} $1 ${RES}"
;;
*)
echo "you inputcolor is not exits"
echo $1
;;
esac
}
plus_color $1 $2
sh textcolor_fun.sh apple red
apple
(3)示例3:利用case来开发类似系统启动rsync服务的脚本(可参考系统的rpcbind和nfs脚本)
a、手动启动rsync服务进行测试
lsof -i :873
rsync --daemon
Failed to parse config file: /etc/rsyncd.conf
touch /etc/rsyncd.conf
rsync --daemon
lsof -i :873
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
rsync 2837 root 4u IPv4 15548 0t0 TCP *:rsync (LISTEN)
rsync 2837 root 5u IPv6 15549 0t0 TCP *:rsync (LISTEN)
pkill rsync
lsof -i :873
b、思路
使用pkill rsync命令来停止rsync并不好,也不规范,最好是通过pid文件来停止rsync服务。同时,如果使用端口或进程进行判断程序是否启动或关闭时,在放到/etc/init.d/下或chkconfig进行管理时,容易出现错误,无法正确执行。
vi /etc/rsyncd.conf
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsyncd.lock
log file = /var/log/rsyncd.log
pkill rsync
lsof -i :873
rsync --daemon
cat /var/run/rsyncd.pid
3026
c、完整脚本
vi /services/scripts/rsyncd.sh
#!/bin/sh
. /etc/init.d/functions
pidfile="/var/run/rsyncd.pid"
start_rsync(){
if [ -f $pidfile ];then
echo "rsync isrunning"
else
rsync --daemon
action "rsyncstart" /bin/true
fi
}
stop_rsync(){
if [ -f $pidfile ];then
kill -USR2 `cat$pidfile`
rm -f ${pidfile}
action "rsync isstopped" /bin/true
else
action "rsync notrunning, already been stopped." /bin/false
fi
}
case "$1" in
start)
start_rsync
RETVAL=$?
;;
stop)
stop_rsync
RETVAL=$?
;;
restart)
stop_rsync
sleep 2
start_rsync
RETVAL=$?
;;
*)
echo "USAGE: $0{start|stop|restart}"
exit 1
;;
esac
exit $RETVAL
scripts]# sh rsyncd.sh stop
rsync not running, already been stopped. [ FAILED ]
sh rsyncd.sh stop
rsync not running
sh rsyncd.sh start
rsync start [ OK ]
sh rsyncd.sh start
rsync is running, already been stopped. [ FAILED ]
sh rsyncd.sh stop
rsync is stopped [ OK ]
sh rsyncd.sh restart
rsync not running, already been stopped. [ FAILED ]
rsync start [ OK ]
d、加入chkconfig进行管理
cp /services/scripts/rsyncd.sh /etc/init.d/rsyncd
chmod +x /etc/init.d/rsyncd
vi /etc/init.d/rsyncd
#!/bin/sh
# chkconfig: 2345 31 61
# description: rsync start and stop scripts
. /etc/init.d/functions
pidfile="/var/run/rsyncd.pid"
start_rsync(){
...
esac
exit $RETVAL
chkconfig --add rsyncd
chkconfig rsyncd on
chkconfig --list rsyncd
rsyncd 0:off 1:off 2:on 3:on 4:on 5:on 6:off
(4)示例4:将前面shell函数的示例6:MySQL启动脚本由函数改为case来实现
cp /services/scripts/singleDBStart.sh/services/scripts/MySQLStart_case.sh
vi /services/scripts/MySQLStart_case.sh
#!/bin/sh
. /etc/init.d/functions
basedir=/application/mysql/
datadir=/application/mysql/data/
function usage(){
echo "$0{start|stop|restart}"
exit 1
}
[ $# -ne 1 ] && usage
function start_mysql(){
$basedir/bin//mysqld_safe--datadir="$datadir" --user=mysql &
if [ $? -eq 0 ]
then
action "startmysql" /bin/true
else
action "startmysql" /bin/false
fi
}
function stop_mysql(){
mysqladmin -uroot-p'123456' shutdown
if [ $? -eq 0 ]
then
action "stopmysql" /bin/true
else
action "stopmysql" /bin/false
fi
}
case "$1" in
start)
start_mysql
;;
stop)
stop_mysql
;;
restart)
stop_mysql
start_mysql
;;
*)
usage
;;
esac
sh MySQLStart_case.sh start
start mysql [ OK ]
sh MySQLStart_case stop
stop mysql [ OK ]
mysql -uroot -p
mysql>
三、while循环和until条件句
1、语法格式
(1)while语法
while 条件
do
指令。。。
done
(2)until语法(应用场合不多见,了解就行)
until条件
do
指令。。。
done
2、while读取文件内容
(1)方法一
cat a.log | while read line
do
done
(2)方法二
while read line
do
done < a.log
(3)方法三
exec < a.log
while read line
do
done
3、示例
(1)示例1:(while)每隔2秒记录一次系统负载情况,屏幕输出
vi /services/scripts/while01.sh
#!/bin/sh
while true
do
uptime
sleep 2 #休眠2秒,usleep 1000000也是休眠1秒,单位是微秒
done
sh while01.sh
22:19:01 up 3:34, 3 users, load average: 0.01, 0.07, 0.12
22:19:03 up 3:34, 3 users, load average: 0.01, 0.07, 0.12
(2)示例2:使用while语句计算1+2+..+100的值
a、方法一
vi /services/scripts/while02.sh
#!/bin/sh
i=1
sum=0
while (( i < 101 ))
do
(( $sum + $i ))
(( i++ ))
done
echo "sum = " $sum
sh while02.sh
sum = 5050
b、方法二
vi /services/scripts/while02_02.sh
#!/bin/sh
i=1
sum=0
while [ $i -le 100 ]
do
let sum=sum+i
let i=i+1
done
echo "sum = " $sum
sh while02_02.sh
sum = 5050
(3)示例3:使用while语句竖向打印10,9,8,...1
vi /services/scripts/while03.sh
#!/bin/sh
i=10
while ((i>0))
do
echo $i
((i--))
done
sh while03.sh
10
9
8
7
6
5
4
3
2
1
(4)示例4:手机充值10元,每发一条短信扣1角5分,当余额低于1角5分不能发短信,提示余额不足,请充值。用while实现
a、思路
单位换算,统一单位,尽量用整数来计算。所以10元=1000分,1角5分=15分。
b、脚本
vi /services/scripts/while04.sh
#!/bin/sh
sum=1000
while ((sum >= 15))
do
((sum=sum-15))
echo "yu e is$sum"
done
echo "yu e bu zhu, qing chong zi"
(5)示例5:计算nginx日志中的所有行的访问字节数的总和
a、nginx日志内容
cat /application/nginx/logs/bbs_access.log
192.168.158.111 - - [18/Mar/2017:04:52:39 +0800] "GET /HTTP/1.1" 200 12 "-" "Mozilla/5.0 (Windows NT6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2824.2Safari/537.36" "-"
192.168.158.111 - - [18/Mar/2017:04:52:39 +0800] "GET/favicon.ico HTTP/1.1" 404 570 "http://bbs.abc.org/""Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/54.0.2824.2 Safari/537.36" "-"
192.168.158.111 - - [18/Mar/2017:04:52:39 +0800] "GET/favicon.ico HTTP/1.1" 404 - "http://bbs.abc.org/""Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (
KHTML, like Gecko) Chrome/54.0.2824.2 Safari/537.36""-"
b、脚本
vi /services/scripts/while05.sh
#!/bin/sh
sum=0
i=0
while read line
do
i=$(echo $line|awk '{print$10}')
if expr $i + 0&>/dev/null
then
((sum=sum+i))
fi
done < /application/nginx/logs/bbs_access.log
echo $sum
sh while05.sh
582
sh -x while05.sh
+ sum=0
+ i=0
+ read line
++ echo 192.168.158.111 - - '[18/Mar/2017:04:52:39' '+0800]''"GET' / 'HTTP/1.1"' 200 12 '"-"' '"Mozilla/5.0''(Windows' NT '6.1)' AppleWebKit/537.36 '(KHTML,' like 'Gecko)'Chrome/54.0.2824.2 'Safari/537.36"' '"-"'
++ awk '{print $10}'
+ i=12
+ expr 12 + 0
+ (( sum=sum+i ))
+ read line
++ echo 192.168.158.111 - - '[18/Mar/2017:04:52:39' '+0800]''"GET' /favicon.ico 'HTTP/1.1"' 404 570'"http://bbs.abc.org/"' '"Mozilla/5.0' '(Windows' NT '6.1)'AppleWebKit/537.36 '(KHTML,' like 'Gecko)' Chrome/54.0.2824.2'Safari/537.36"' '"-"'
++ awk '{print $10}'
+ i=570
+ expr 570 + 0
+ (( sum=sum+i ))
+ read line
++ echo 192.168.158.111 - - '[18/Mar/2017:04:52:39' '+0800]''"GET' /favicon.ico 'HTTP/1.1"' 404 -'"http://bbs.abc.org/"' '"Mozilla/5.0' '(Windows' NT '6.1)'AppleWebKit/537.36 '('
++ awk '{print $10}'
+ i=-
+ expr - + 0
+ read line
++ echo KHTML, like 'Gecko)' Chrome/54.0.2824.2'Safari/537.36"' '"-"'
++ awk '{print $10}'
+ i=
+ expr + 0
+ read line
+ echo 582
582
(6)示例6:以守护进程方式,每10秒,实现一次rsync mysqlbinlog推送
a、思路
crond定时任务,只能精确到分钟,无法到秒级,所以只能通过循环来实现。脚本完成后,放入rc.local文件中,让脚本开机自启动运行。
b、脚本
vi /services/scripts/while06.sh
#!/bin/sh
while true
do
rsync -az/data/3306/mysql-bin* rsync_backup@192.168.1.8::backup--password-file=/etc/rsync.password
sleep 10
done
sh while06.sh &
c、开机自启动
echo "/bin/sh /services/scripts/while06.sh &" >>/etc/rc.local
4、条件句小结
(1)while循环的特长是执行守护进程以及希望循环不退出,用于频率小于1分钟的循环处理。其它的while循环几乎都可以用for循环替代。
(2)case语句可以用if语句替换,一般在系统启动脚本传入少量固定规则字符串时用case,其它判断多用if。
(3)if和for语句是最常用的,其次是while(守护进程),再就是case(服务启动脚本)
四、for循环
1、语法格式
(1)for循环结构
for 变量名 in 变量取值列表
do
指令。。。
done
说明:“in 变量取值列表”可以省略,当省略时,默认为$@($@表示获取当前执行的shell的所有参数)。也就是for i 相当于for i in "$@"
(2)C语言型语法的for循环结构
for((exp1;exp2;exp3))
do
指令。。。
done
2、示例
(1)示例1:通过for循环打印5 4 3 2 1
vi /services/scripts/for01.sh
#!/bin/sh
for n in 5 4 3 2 1 # 也可以用for n in {5..1}
do
echo $n
done
(2)示例2:通过for循环只保留crond、sshd、rsyslog、network,关闭其它开机自启动,
vi /services/scripts/for02_autoStart.sh
#!/bin/sh
LANG=en
for name in `chkconfig --list | grep 3:on | awk '{print $1}'`
do
chkconfig $name off
done
for name in sshd crond rsyslog network sysstat
do
chkconfig $name on
done
(3)示例3:通过for循环计算1+2+..+100
vi /services/scripts/for03_add.sh
#!/bin/sh
sum=0
for i in {1..100} # 也可以for ((i=0;i<=100;i++))
do
((sum=sum+i))
done
echo $sum
(4)示例4:创建10个文件,文件名中包含oldboy,结尾是1到10,扩展名为html
vi /services/scripts/for04_createFiles.sh
#!/bin/sh
for ((i=1;i<11;i++))
do
touch oldboy_${i}.html
done
sh for04_createFiles.sh
ll
-rw-r--r-- 1 root root 0Jun 4 22:26 oldboy_1.html
-rw-r--r-- 1 root root 0Jun 4 22:26 oldboy_10.html
-rw-r--r-- 1 root root 0Jun 4 22:26 oldboy_2.html
-rw-r--r-- 1 root root 0Jun 4 22:26 oldboy_3.html
-rw-r--r-- 1 root root 0Jun 4 22:26 oldboy_4.html
-rw-r--r-- 1 root root 0Jun 4 22:26 oldboy_5.html
-rw-r--r-- 1 root root 0Jun 4 22:26 oldboy_6.html
-rw-r--r-- 1 root root 0Jun 4 22:26 oldboy_7.html
-rw-r--r-- 1 root root 0Jun 4 22:26 oldboy_8.html
-rw-r--r-- 1 root root 0Jun 4 22:26 oldboy_9.html
(5)示例5:通过for循环将上述文件名中的oldboy全部改为linux,扩展名改为大写HTML
a、方法一
vi /services/scripts/for05_rename.sh
#!/bin/sh
cd /services/scripts/for
for n in `seq 10`
do
mv oldboy_${i}.htmllinux_${i}.HTML
done
b、方法二
vi /services/scripts/for05_rename.sh
#!/bin/sh
cd /services/scripts/for
for f in `ls *.html`
do
#mv $f `echo $f | sed 's#oldboy#linux#g' |sed 's#html#HTML#g'`
mv $f `echo $f | sed's#oldboy\(.*\).html#linux\1.HTML#g'`
done
(6)示例6:通过for循环批量创建10个系统账号oldboy01到oldboy10,密码不能相同
a、思路和技术点
(i)数字前面加0
数字前面加0,有多种方法。可参参考http://oldboy.blog.51cto.com/2561410/788422
最常用的是seq的-w参数
seq -w 10
01
02
03
04
05
06
07
08
09
10
(ii)无交互式设置密码
echo aaa | passwd --stdin test
b、脚本
vi /services/scripts/for06_createUsers01.sh
#!/bin/sh
for n in `seq -w 10`
do
useradd oldboy$n && echo "root$n" | passwd--stdin oldboy$n
#useradd oldboy$n -p "root$n" #也可以这样创建用户时直接设置密码但/etc/shadow中为明文
done
(7)示例7:通过for循环批量创建10个系统账号oldboy01到oldboy10,密码为随机8位字符串
a、随机数
(i)echo $RAMDOM:一般为5位数以下
echo $RAMDOM
1634
echo $RANDOM
13621
echo $RANDOM
18892
echo $RANDOM
620
(ii)RAMDOM加上一个8位数10000000:echo$((RANDOM+10000000))
echo $((RANDOM+10000000))
10030175
echo $((RANDOM+10000000))
10012580
echo $((RANDOM+10000000))
10016722
(iii)echo $RAMDOM后md5sum生成的字符串取任意8位:
echo $RANDOM | md5sum | cut -c 1-8
785c4f64
echo "`date`$RANDOM" | md5sum | cut -c 1-8 #以时间为种子取随机数
adba4d85
b、脚本
vi /services/scripts/for07_createUsers02.sh
#!/bin/sh
for n in `seq -w 10`
do
pass=`echo $RANDOM | md5sum | cut -c 1-8`
useradd oldboy$n && echo "$pass" | passwd--stdin oldboy$n
echo -e "oldboy$n\t $pass ">> pass.txt
done
五、随机数的6种获取方法
1、方法一:RANDOM
echo $RANDOM
18682
2、方法二:openssl
openssl rand -base64 8
QFN20JhyWCI=
openssl rand -base64 10
YJzbf9BJjm+4lg==
3、方法三:date
date +%s%N
1496592770592461116
4、方法四:urandom
head /dev/urandom | cksum
1232898784 2017
5、方法五:uuid
cat /proc/sys/kernel/random/uuid
9e1960ac-69c9-4917-bbe5-d1926bafaae0
6、方法六:expect的mkpasswd方法
mkpasswd -l 8
So5;a9i
mkpasswd --help
mkpasswd [args] [user]
where arguments are:
-l # (length of password,default = 9)
-d # (min # of digits,default = 2)
-c # (min # of lowercasechars, default = 2)
-C # (min # of uppercasechars, default = 2)
-s # (min # of special chars,default = 1)
-v (verbose, show passwdinteraction)
-p prog (program to setpassword, default = passwd)
六、循环的控制
1、break、continue、exit和return的对比
命令 | 说明 |
break n | n表示跳出循环的层数,如果省略n则表示跳出整个循环 |
continue n | n表示退出到第n层继续循环,如果省略n则表示跳过本次循环,忽略剩余代码,进入下一次循环 |
exit n | 退出当前shell程序,n为返回值。n也可以省略,再下一个shell里通过$?来接收这个n的值 |
return n | 用于在函数里做为函数的返回值,用于判断函数执行是否正确 |
2、示例
(1)示例1:break跳出整个循环体,执行循环体外后面的语句
vi break01.sh
#!/bin/sh
for ((i=0;i<=5;i++))
do
if [ $i -eq 3 ];then
break
fi
echo $i
done
echo "OK"
sh break01.sh
0
1
2
OK
(2)示例2:continue当前循环,执行下一次循环
vi continue01.sh
#!/bin/sh
for ((i=0;i<=5;i++))
do
if [ $i -eq 3 ];then
continue
fi
echo $i
done
echo "OK"
sh break01.sh
0
1
2
4
5
OK
(3)示例3:exit退出shell,不在执行程序体中任何语句
vi exit01.sh
#!/bin/sh
for ((i=0;i<=5;i++))
do
if [ $i -eq 3 ];then
exit
fi
echo $i
done
echo "OK"
sh break01.sh
0
1
2
(4)示例4:用shell脚本给服务器配置临时别名IP,并可以随进撤销配置的所有IP。IP段为192.168.2.1-192.168.2.16,其中192.168.2.10不能配置。
vi ipconfig.sh
#!/bin/sh
for ((i=1;i<17;i++))
do
if [ $i -eq 10 ];then
continue
fi
ifconfig eth0:$i 192.168.2.$i/24 up
done
(5)示例5:判断当前局域网网络里,当前在线用户的IP有哪些?
vi ping01.sh
#!/bin/sh
for n in {1..254}
do
ping -c1 192.168.1.$n >/dev/null 2>&1
if [ $? -eq 0 ]
then
echo "192.168.1.$n is up " >> /tmp/uplist.log
else
echo "192.168.1.$n is down " >> /tmp/downlist.log
fi
done
(6)示例6:判断是否有DDOS攻击
vi ddos.sh
#!/bin/sh
while true
do
awk '{print $1}' access.log|grep -v"^$"|sort|uniq -c >/tmp/tmp.log
exec </tmp/tmp.log
while read line
do
ip=`echo $line|awk '{print $2}'`
count=`echo $line|awk '{print $1}'`
if [ $count -gt 3 ] && [ `iptables-L -n |grep "$ip"|wc -l` -lt 1 ]
then
iptables -I INPUT -s $ip -j DROP
echo "$line is dropped" >>/tmp/droplist.log
fi
done
sleep 5
done
七、一键优化脚本
1、一键优化的思维思想
(1)要知道在哪些优化点
(2)每个优化点,如何优化?
(3)针对每个优化点,在命令行完成单条测试
2、示例1:一键优化Linux系统的脚本
(1)优化点
a、安装系统时精简安装包(最小化安装)
b、配置yum源
c、禁用开机不需要的启动的服务
d、优化系统内核参数/etc/sysctl.conf
e、增加系统文件描述符、堆栈等配置
f、禁止root远程登陆,修改SSH端口为特殊端口,禁止DNS,禁止空密码
g、有外网IP的要配置防火墙,公对外开启需提供服务的端口,配置或关闭SELinux
h、清除无用的默认用户和组(非必须),添加运维成员用户
i、锁定敏感文件,如/etc/passwd(非必须)
j、配置时间同步
k、配置sudo,对普通用户权限精细控制
3、示例2:检查一键优化是否成功的脚本
八、Shell数组
1、定义
(1)方法一(常用)
array=(value1 value2 value3 ...)
(2)方法二(常用)
array=($(ls等命令))
(3)方法三
array=([key1]=value1 [key2]=value2...)
(4)方法四
array[下标1]=value1
array[下标2]=value2
(5)方法五
declare -a array
2、数据的操作
(1)获取数组长度
echo ${#array[@]}或echo ${#array[*]}
(2)打印单个数组元素:数组下标从0开始
echo ${array[下标]}
(3)打印数组全部元素
echo ${array[@]}或echo${array[*]} #与获取数组长度相比,没有#号
(4)数组的赋值
数组的赋值是直接通过数据名[下标]就可以对其进行引用赋值。如果下标不存在,则会自动添加一个新的数组元素,如查存在就覆盖原来的值。
array[下标]=value4
(5)数组的删除
通过unset 数组名[下标]就可以清除对应的元素,如不带下标,则删除整个数组。就相当于unset 变量,可以理解为把数组名当变量在使用。
unset array[1]
unset array
(6)数组的截取(与变量子串的替换相似)
a、截取指定长度的子串
array[@或*]:开始下标: 截取长度
b、从指定下标截取到结尾
array[@或*]:开始下标
(7)数组的替换(与变量子串的替换相似,该操作临时生效,不会改就原数组内容,类似sed)
array[@或*]/需替换的内容/替换内容
3、示例
(1)示例1:定义数组
array=(1 2 3)
(2)示例2:获取数组array的长度
echo ${#array[@]}
3
echo ${#array[*]}
3
(3)示例3:打印数组array中单个的元素
echo ${array[0]}
1
echo ${array[1]}
2
echo ${array[2]}
3
(4)示例4:打印数组array中所有的元素
echo ${array[*]}
1 2 3
echo ${array[@]}
1 2 3
(5)示例5:数组赋值
a、为数组array新增一个元素
array[3]=4
b、查看数组所有元素
echo ${array[@]}
1 2 3 4
c、修改数组第一个元素的值
array[0]=test
d、查看数组所有元素
echo ${array[*]}
test 2 3 4
(6)示例6:删除数组
a、删除数组array第一个元素
unset array[0]
echo ${array[*]}
2 3 4
echo ${#array[*]}
2 # <--数组长度为2
echo ${array[0]}
# <--下标为0的元素,已经不存在了,下标从1开始了
echo ${array[1]}
2
b、删除数组array
unset array
echo ${array[*]}
# <--为空
(7)示例7:数组的截取
array=(1 2 3 4 5)
echo ${array[@]:2:2}
3 4
echo ${array[@]:2}
3 4 5
(8)示例8:数组的替换
array=(1 2 3 4 5)
echo ${array[@]/2/test}
1 test 3 4 5
echo ${array[@]}
1 2 3 4 5
array1=(${array[@]/2/test})
echo ${array1[@]}
1 test 3 4 5
(9)示例9:方法二方式定义数组的相关操作
pwd
/services/scripts
ls
a1.log a2.log a3.log a4.log a5.log
array2=($(ls))
echo ${#array2[*]}
5
echo ${array2[0]}
a1.log
echo ${array2[1]}
a2.log
for f in ${array2[*]};do echo $f;done
a1.log
a2.log
a3.log
a4.log
a5.log
(10)示例10:定义数组并打印
array=(
aa
bb
cc
)
for ((i=0;i<${#array[*]};i++))
do
echo "This is num $i,then content is ${array[$i]}"
done
4、数组小结:常用功能
(1)数组定义
array=(1 3 3)
array=($(ls))
(2)数组打印
a、打印所有元素
${array[*]}
b、打印数组长度
${#array[*]}
c、打印单个元素
${array[i]}
九、运维要求必会的脚本开发
1、各类监控脚本。如:内存、磁盘、端口、URL监控报警
2、如果监控网站目录文件是否被篡改,以及站点目录被篡改后如何批量恢复
3、如何开发各类服务rsync、nginx、mysql等启动及停止专业脚本
4、如何开发MySQL主从同步监控报警以及自动化处理不同步的脚本
5、一键配置mysql多实例,一键配置mysql主从同步,一键部署脚本
6、监控http/MySQL/rsync/nfs等服务是否正常生产脚本
7、一键软件安装以及优化,lamp、lnmp、一键安装,一键安装数据库,优化配置主从
8、MySQL多实例启动脚本,分库,分表自动化备份脚本
9、监控网络连接数,以及根据webPV分IP脚本
10、监控网站pv以及流量。并对流量信息进行统计
11、监控web服务器URL地址的脚本,可以批量以及通用
12、系统的基础优化,一键优化脚本
13、清理系统垃圾文件(过期备份)脚本
14、tcp连接状态统计
15、批量创建用户并设置随机8位密码
16、获取服务器的信息,批量分文件
十、Shell脚本的调试
1、常见脚本错误
(1)if条件句的条件表达式中中括号[]两边缺少空格
cat if.sh
#!/bin/bash
if [$1-lt$2]#<--[和$1之间缺少空格
then
echo "Yes,$1islessthan$2"
exit
fi
shif.sh12
if.sh:line2:[1:commandnotfound#<--把[1当成一个命令了,提示命令没有找到
(2)if条件句的条件表达式中应成对出现的符号,缺失一个
cat if.sh
#!/bin/bash
if [ $1-lt$2#<--[]应成对出现,但缺少右边的]
then
echo "Yes,$1islessthan$2"
exit
fi
shif.sh12
if.sh:line2:[:missing`]'#<--但缺少右边的]
(3)if条件句缺少结尾的fi
cat if.sh
#!/bin/bash
if[$1-lt$2]
then
echo "Yes,$1islessthan$2"
exit
#<--缺少if结束的fi
shif.sh12
if.sh:line6:syntaxerror:unexpectedendoffile#<--语法错误,未预期的文件结尾。
2、调试技巧
(1)调试要求
a、重视书写习惯、开发规范和开发制度,尽量减少脚本调试的难度,提升开发效率
b、基本语法要熟悉,才能利用好调试
c、开发时,思路要清晰,将复杂的脚本简单化,分段实现
d、将重复的内容,最好写成函数
(2)技巧1:使用dos2unix命令处理来自windows下开发的脚本
从windows编辑的脚本到linux下时,有时候会有格式问题,存在一些不可见字符,导致脚本运行时报错。由于是不可见字符,用眼睛看代码时,又不容易发现问题。所以,从windows迁移到linux上的脚本或者不是自已写的脚本,一律要用dos2unix命令格式化处理后再运行。例:下面脚本是从windows开发的,在linux中显示的结果,每行结尾处多了^M
cat -vwhile-debug-test.sh
#!/bin/sh^M
i=1^M
sum=0^M
while((i<=100))^M
do^M
((sum=sum+i))^M
((i++))^M
done^M
echo $sum^M
dos2unix while-debug-test.sh
dos2unix:convertingfilewhile-debug-test.shtoUNIXformat..
(3)技巧2:使用echo命令调试
在指定位置插入echo命令,进行输出,并在语句后面加上exit命令,不执行echo后面的语句,这样就可以逐段排查了(写法:echo$var;exit)。如下例:
vi/services/scripts/webSiteCheck.sh
#!/bin/sh
path=/server/scripts
while true
do
f=`md5sum -c $path/md5sum.db2>/dev/null|grepFAILED|wc-l`
n=`cat $path/check_site_filenumber.log|wc-l`
findtext=`find /application/nginx/html/-type f>$path/new_site.log`
log=/tmp/check.log
[ !-f$log ]&&touch$log
if[ $f-ne0 ]||[ `cat$path/new_site.log|wc-l`-ne$n ]
then
echo "`md5sum-c md5sum.db2>/dev/null|grepFAILED`">$log
echo "`md5sum -c md5sum.db2>/dev/null|grepFAILED`"#调试,echo 输出
exit#<--调试,退出,不执行后面的语句
diff $path/check_site_filenumber.log$path/new_site.log>>$log
mail -s "siteistampered$(date)"aaa@abc.com<$log
fi
sleep180
done
3、bash的调试参数
(1)-n参数:不执行脚本,仅查询脚本是否存在语法问题,如果有错误,则给出错误提示
sh-n while01.sh
#<--正确,无任何提示,脚本也未执行
sh-n if.sh
if.sh:line6:syntaxerror:unexpectedendoffile#<--有错误提示,脚本也未执行
(2)-v参数:在执行脚本时,先将脚本的内容输出到屏幕上,然后再执行脚本,如果有错误,则给出错误提示
sh-v while02.sh
#!/bin/sh
i=1
sum=0
while(($i<101))
do
sum=$(($sum+$i))
i=$(($i+1))
done
echo "sum="$sum#<--文件内容
sum=5050#<--文件执行的结果
(3)-x参数:将执行的脚本内容和输出显示到屏幕上(最常用的参数)
sh -x while01.sh
+true
+uptime
+sleep2
4、使用set命令调试部分脚本内容(缩小调试的作用域)
bash-x将会调试脚本全部内容,但有时,我们不想调试全部脚本,只想调试部分脚本,这就需要set命令了。如脚本调用了系统函数,但系统函数不会有错误,我们不需要调试,这时就可以用set-x和set+x来指定要调试的作用域了。
(1)参数
-n:读命令,但不执行
-v:显示读取的所有行
-x:显示所有命令及其参数
(2)示例
viwhile02.sh
#!/bin/sh
i=1
sum=0
set -x#<--调试开始位置
while(($i<101))
do
sum=$(($sum+$i))
i=$(($i+1))
done
set +x#<--调试结束位置
echo "sum="$sum
sh while02.sh#<--不需要用sh-x
更多推荐
所有评论(0)