Shell学习笔记(四):必备知识篇
循环控制及状态返回值的应用实践
break、continue、exit、return的区别和对比
例1:break跳出整个循环,执行循环下面的其他程序
#!/bin/sh
for((i=0;i<=5;i++))
do
if [ $i -eq 3 ];then
#continue;
break;
#exit
fi
echo $i
done
echo "ok"
例2(生产场景):开发shell脚本实现给服务器临时配置多个别名IP,并可以随时撤销配置的所有
IP。IP地址为:10.0.2.1-10.0.2.16,其中10.0.2.10不能设置。
#!/bin/sh
[ -f /etc/init.d/functions ] && . /etc/init.d/functions
RETVAL=0
op(){
if [ "$1" == "del" ];then
list=`echo {16..1}`
else
list=`echo {1..16}`
fi
for ip in $list;do
if [ $ip -eq 10 ];then
continue
fi
ip addr $1 10.0.2.$ip/24 dev ens33 label ens33:$ip &>/dev/null
RETVAL=$?
if [ $RETVAL -eq 0 ];then
action "$1 $ip" /bin/true #此处的提示用通用的$1,传参来控制
else
action "$1 $ip" /bin/false
fi
done
return $RETVAL
}
case "$1" in
start)
op add #启动时,就传参add给op函数
RETVAL=$?
;;
stop)
op del #停止时,就传参del给op函数
RETVAL=$?
;;
restart)
op del
sleep 2
op add
RETVAL=$?
;;
*)
printf "USAGE:$0 {start|stop|restart}\n"
esac
exit $RETVAL
shell函数语法
语法格式
简单的语法:
函数名(){
指令...
return n
}
规范的语法:
function 函数名(){
指令...
return n
}
shell函数执行
调用函数:
1)直接执行函数名即可。注意,不需要带小括号。
函数名
2)带参数的函数执行方法:
函数名 参数1 参数2
函数带参数的说明
- 在函数体中位置参数($1、$2、$3、$4、$5、$#、$*、$?以及$@)都可以是函数参数
- 父脚本的参数则临时地被函数参数所掩盖或隐藏
- $0比较特殊,他仍然是父脚本的名称
- 当函数完成时,原来的命令行参数会恢复
- 在shell函数里面,return命令的功能与工作方式与exit相同,用于跳出函数
- 在shell函数体里使用exit会终止整个shell脚本
- return语句会返回一个退出值给调用的程序。
例:编写shell开发linux系统一键优化脚本。
1、安装系统是最小化安装
2、配置国内高速yum源
3、禁用开机不需要启动的服务
4、优化系统内核参数/etc/sysctl.conf
5、增加系统文件描述符、堆栈等配置
6、禁止root远程登录。修改ssh端口为特殊端口,禁止DNS,空密码
7、有外网ip的机器要开启配置防火墙,仅对外开启需要提供服务的端口,配置或关闭selinux
8、清除无用的默认系统账户或组(非必须)(添加运维的用户)
9、锁定敏感文件,如/etc/passwd(非必须)
10、配置服务器和互联网时间同步
11、初始化用户,并配置sudo对普通用户权限的控制
12、修改系统字符集
13、补装系统软件及升级系统到最新
#!/bin/bash
export PATH=$PATH:/bin:/sbin:/usr/sbin
# Require root to run this script.
if [ "$UID" != "0" ]; then
echo "Please run this script by root."
exit 1
fi
#define cmd var
SERVICE=`which service`
CHKCONFIG=`which chkconfig`
function mod_yum(){
#modify yum path
if [ -e /etc/yum.repos.d/CentOS-Base.repo ]
then
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup&&\
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.sise.edu.cn/repo/CentOS-Base.repo
fi
}
function close_selinux(){
#1.close selinux
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
#grep SELINUX=disabled /etc/selinux/config
setenforce 0 &>/dev/null
#getenforce
}
function close_firewall(){
#2.close firewall
systemctl stop firewalld
systemctl stop firewalld
systemctl disable firewalld
}
function least_service(){
#3.least service startup
chkconfig|awk '{print "chkconfig",$1,"off"}'|bash
chkconfig|egrep "crond|sshd|network|rsyslog|sysstat"|awk '{print "chkconfig",$1,"on"}'|bash
#export LANG=en
#chkconfig --list|grep 3:on
}
function adduser(){
#4.add yjscloud and sudo
if [ `grep -w yjscloud /etc/passwd|wc -l` -lt 1 ]
then
useradd yjscloud
echo 123456|passwd --stdin yjscloud
\cp /etc/sudoers /etc/sudoers.ori
echo "yjscloud ALL=(ALL) NOPASSWD: ALL " >>/etc/sudoers
tail -1 /etc/sudoers
visudo -c &>/dev/null
fi
}
function charset(){
#5.charset config
cp /etc/locale.conf /etc/locale.conf
echo 'LANG="zh_CN.UTF-8"' >/etc/locale.conf
source /etc/locale.conf
#echo $LANG
}
function time_sync(){
#6.time sync.
cron=/var/spool/cron/root
if [ `grep -w "ntpdate" $cron|wc -l` -lt 1 ]
then
echo '#time sync by yjscloud at 2018-6-24' >>$cron
echo '*/5 * * * * /usr/sbin/ntpdate cn.pool.ntp.org >/dev/null 2>&1' >>$cron
crontab -l
fi
}
function com_line_set(){
#7.command set.
if [ `egrep "TMOUT|HISTSIZE|HISTFILESIZE" /etc/profile|wc -l` -ge 3 ]
then
echo 'export TMOUT=300' >>/etc/profile
echo 'export HISTSIZE=5' >>/etc/profile
echo 'export HISTFILESIZE=5' >>/etc/profile
. /etc/profile
fi
}
function open_file_set(){
#8.increase open file.
if [ `grep 65535 /etc/security/limits.conf|wc -l` -lt 1 ]
then
echo '* - nofile 65535 ' >>/etc/security/limits.conf
tail -1 /etc/security/limits.conf
fi
}
function set_kernel(){
#9.kernel set.
if [ `grep kernel_flag /etc/sysctl.conf|wc -l` -lt 1 ]
then
cat >>/etc/sysctl.conf<<EOF
#kernel_flag
net.ipv4.tcp_fin_timeout = 2
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_keepalive_time = 600
net.ipv4.ip_local_port_range = 4000 65000
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_max_tw_buckets = 36000
net.ipv4.route.gc_timeout = 100
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_synack_retries = 1
net.core.somaxconn = 16384
net.core.netdev_max_backlog = 16384
net.ipv4.tcp_max_orphans = 16384
EOF
sysctl -p
fi
}
function init_ssh(){
\cp /etc/ssh/sshd_config /etc/ssh/sshd_config.`date +"%Y-%m-%d_%H-%M-%S"`
#sed -i 's%#Port 22%Port 52113%' /etc/ssh/sshd_config
sed -i 's%#PermitRootLogin yes%PermitRootLogin no%' /etc/ssh/sshd_config
sed -i 's%#PermitEmptyPasswords no%PermitEmptyPasswords no%' /etc/ssh/sshd_config
sed -i 's%#UseDNS yes%UseDNS no%' /etc/ssh/sshd_config
/etc/init.d/sshd reload &>/dev/null
}
function update_linux(){
#10.upgrade linux.
if [ `rpm -qa lrzsz nmap tree dos2unix nc|wc -l` -le 3 ]
then
yum install lrzsz nmap tree dos2unix nc ntpdate -y
#yum update -y
fi
}
main(){
mod_yum
close_selinux
close_firewall
least_service
adduser
charset
time_sync
com_line_set
open_file_set
set_kernel
init_ssh
update_linux
}
main
shell数组
简单的说,数组就是相同数据类型的元素按一定顺序排列的集合。数组就是把有限个类型相同的变量用一个名字命名,然后用编号区分他们的变量的集合。这个名字成为数组名,编号成为下标。组成数组的各个变量成为数组的分量,也称为数组的元素,有时也称为下标变量。如果有过用其它语言编程的经历,那么想必会熟悉数组的概念。由于有了数组,可以用相同名字引用一系列变量,并用数字(索引)来识别它们。在许多场合,使用数组可以缩短和简化程序,因为可以利用索引值设计一个循环,高校处理多种情况。
数组定义与读取
方法1:array=(value1 value2 value3)
1)定义数组
[root@pan ~]# array=(1 2 3) #对括号表示数组,数组元素用“空格”符号分割开
2)获取数组的长度
[root@pan ~]# echo ${#array[@]} #用${#数组名[@或*]}可以获得到数组长度
3
[root@pan ~]# echo ${#array[*]}
3
3)打印数组元素
[root@pan ~]# echo ${array[0]} #打印数组元素用${数组名[下标]}下标是从0开始
1
[root@pan ~]# echo ${array[1]}
2
[root@pan ~]# echo ${array[2]}
3
[root@pan ~]# echo ${array[3]} #超出范围输出为空
[root@pan ~]# echo ${array[*]} #下标是:*或者@得到整个数组内容
1 2 3
[root@pan ~]# echo ${array[@]} #把前面去数组长度的#号去掉,例如:${array[@]}
1 2 3
4)数组赋值
直接通过数组名[下标]就可以对其进行引用赋值,如果下标不存在,自动添加新一个数组元素,如果存在就覆盖原来的值
[root@pan ~]# array[3]=4
[root@pan ~]# echo ${array[@]}
1 2 3 4
[root@pan ~]# array[0]=pan
[root@pan ~]# echo ${array[@]}
pan 2 3 4
5)数组删除
直接通过:unset数组[下标]可以清除相应的元素,不带下标,清除整个数据
[root@pan ~]# echo ${array[@]}
pan 2 3 4
[root@pan ~]# unset array #删除整个数组
[root@pan ~]# echo ${array[@]}
[root@pan ~]# array=(1 2 3)
[root@pan ~]# unset array[0] #删除某个数组元素
[root@pan ~]# echo ${array[@]}
2 3
6)数组内容的截取和替换(和前文变量子串的替换很像)
截取:
[root@pan ~]# array=(1 2 3 4 5)
[root@pan ~]# echo ${array[@]:1:3} #截取1号到3号数组元素
2 3 4
[root@pan ~]# echo ${array[@]:3:2}
4 5
替换:
[root@pan ~]# echo ${array[@]/5/6} #把数组中的5替换成6,临时生效,原数组未被修改。和sed很像。
1 2 3 4 6
[root@pan ~]# array1=(${array[@]/5/6})
[root@pan ~]# echo ${array[@]}
1 2 3 4 5
调用方法是:${数组名[@或*]/查找字符/替换字符}
该操作不会改变原先数组内容,如果需要修改,可以看上面例子,重新定义数组
删除:
[root@pan ~]# array1=(one two three four five)
[root@pan ~]# echo ${array1[@]}
one two three four five
[root@pan ~]# echo ${array1[@]#o} #左边开始最短的匹配
ne two three four five
[root@pan ~]# echo ${array1[@]#fo} #左边开始最短的匹配
one two three ur five
[root@pan ~]# echo ${array1[@]%t*e}
one two four five
[root@pan ~]# echo ${array1[@]%%t*e}
one two four five
提示:数组也是变量,因此也适合于前面讲解过的变量的子串处理的功能应用。
方法2:array=([1]=one [2]=two [3]=three)
[root@pan ~]# array=([1]=one [2]=two [3]=three)
[root@pan ~]# echo ${#array[*]}
3
[root@pan ~]# echo ${array[*]}
one two three
方法3:array[0]=a array[1]=b array[2]=c
[root@pan ~]# array[0]=a
[root@pan ~]# array[1]=b
[root@pan ~]# array[2]=c
[root@pan ~]# array[3]=d
[root@pan ~]# echo ${array[0]}
a
[root@pan ~]# echo ${array[2]}
c
数组实践
例1:通过列举法打印数组元素
array=(
xwq
xdf
yjs
)
for ((i=0; i<${#array[*]};i++));do
echo "This is num $i,then content is ${array[$i]}"
done
echo ------------
echo "array len:${#array[*]}"
执行效果:
[root@pan shell]# sh array01.sh
This is num 0,then content is xwq
This is num 1,then content is xdf
This is num 2,then content is yjs
------------
array len:3
例2:把系统命令结果做为数组元素
#!/bin/sh
dir=($(ls))
for ((i=0;i<`echo ${#dir[@]}`;i++));do
echo ${dir[$i]}
done
例3:批量检查多个网站地址是否正常
要求:
1)使用shell数组的方法实现,检测策略尽量模拟用户访问
2)每10秒进行一次全部检测,无法访问的输出做出报警
#!/bin/bash
. /etc/init.d/functions
check_count=0
url_list=(
blog.yjscloud.com
mirrors.sise.edu.cn
yjsxwq.com
192.168.0.15
)
function wait()
{
echo -n '3秒后,执行检查URL操作.';
for ((i=0;i<3;i++)); do
echo -n ".";sleep 1
done
echo
}
function check_url()
{
wait
for ((i=0; i<`echo ${#url_list[*]}`; i++));do
wget -o /dev/null -T 3 --tries=1 --spider ${url_list[$i]} >/dev/null 2>&1
if [ $? -eq 0 ];then
action "${url_list[$i]}" /bin/true
else
action "${url_list[$i]}" /bin/false
fi
done
((check_count++))
}
main(){
while true
do
check_url
echo "-------check count:${check_count}---------"
sleep 7
done
}
main
脚本语法检查
对脚本的调试如果把windows下编辑的脚本放置到linux下执行,最好执行dos2unix
格式化一下,执行dos2unix
格式化是一个很好的习惯。如果没有安装dos2unix
,则使用yum -y install dos2unix
进行安装。使用echo调试,echo命令是最有用的调试脚本的工具之一。一般应在可能出现问题的脚本的重要部分加入echo命令。例如在变量读取或修改操作的前后加入echo命令,并紧挨着退出命令exit。
使用bash命令参数调试
参数说明:
-n:不会执行该脚本,仅查询脚本语法是否有问题,并给出错误提示。
-v:在执行脚本时,先将脚本的内容输出到屏幕上,然后执行脚本,如果有错误,也会给出错误提示。
-x:将执行的脚本内容及输出显示到屏幕上,这对调试很有用的参数。
使用set命令调试部分脚本内容
set命令也可以用于辅助脚本调试,下面是set命令常用的调用选项
set -n:读命令但并不执行
set -v:显示读取的所有行
set -x:显示所有命令及其参数
提示:通过set -x
命令开启调试功能,而通过set +x
关闭调试功能,和bash -x
相比set -x
可以缩小调试的作用域。
例:打印九九乘法表的简版的脚本
#!/bin/sh
set -x
for a in `seq 9`
do
for b in `seq 9`
do
[ $a -ge $b ] && echo -en "$a x $b = $(expr $a \* $b) "
done
set +x
echo " "
done
小结:
1)直接执行脚本根据报错来调试
2)要记得首先用dos2unix
处理脚本
3)sh -x/-n
调试整个脚本
4)set -x
和set +x
调试部分脚本的执行过程(脚本中设置)
5)可通过echo命令输出脚本中要确认的变量及相关内容;然后紧跟着使用exit退出,不执行后面程
序,这种方式便于一步步跟踪脚本。对于逻辑错误的调试比较好用写法即echo $var;exit
6)最关键的还是要语法熟练,养成良好的编码习惯。
作者:废权
链接:https://blog.yjscloud.com/archives/78
声明:如无特别声明本文即为原创文章仅代表个人观点,版权归《废权的博客》所有,欢迎转载,转载请保留原文链接。


共有 0 条评论