通过脚本案例学习shell(五)
通过创建DNS脚本一步一步教你将一个普通脚本规范到一个生产环境脚本
版权声明:
本文遵循“署名非商业性使用相同方式共享 2.5 中国大陆”协议
您可以自由复制、发行、展览、表演、放映、广播或通过信息网络传播本作品
您可以根据本作品演义自己的作品
您必须按照作者或者许可人指定的方式对作品进行署名。
您不得将本作品用于商业目的。
如果您改变、转换本作品或者以本作品为基础进行创作,您只能采用与本协议相同的许可协议发布基于本作品的演绎作品。
对任何再使用或者发行,您都必须向他人清楚地展示本作品使用的许可协议条款。
如果得到著作权人的许可,您可以不受任何这些条件的限制。
Designed by 小诺(www.rsyslog.org dreamfire.blog.51cto.com)
需求:编写一个shell脚本,要求能够自动搭建DNS服务器,并且能够在非交互式下创建区域文件。有关dns搭建可参看"RHEL6.3 DNS配置详解一 DNS相关概念理解及配置基础http://dreamfire.blog.51cto.com/418026/1091943 "
为了能够很好的学习shell脚本的规范书写,这里将搭建过程分为四个步骤,其中每个步骤都是上个步骤的延续,直至创建出生产环境下可以使用的脚本,也就是任何一个人在未知此脚本内容的情况下都可以很好的运行此脚本.
步骤1、先通过手动搭建DNS服务,然后将执行的命令复制到脚本文件中,创建一个简单脚本。
1、安装bind、bind-chroot包
- yum install bind bind-chroot -y
2、修改/etc/named.conf文件
- sed -i ‘s/127\.0\.0\.1/any/’ /etc/named.conf //替换127.0.0.1为any
- sed -i 's/::1/any/' /etc/named.conf //替换::1为any
- sed -i 's/localhost/any/' /etc/named.conf //替换localhost为any
- #sed -i -e '/listen-on/d' -e '/allow-query/d' -e '/dnssec/d' /etc/named.conf //将以上sed的三行删掉,dns默认为any,也可以实现。
- sed -i 's/^dnssec/\/\/&/' /etc/named.conf //将以dnssec开头的所有行前面加\\,其中&是引用前面的替换值,\/是转义
- 注意:使用sed命令的时候,先不要-i参数,可以通过-n和p函数先在屏幕上显示是否正常,例:sed -n ‘s/127\.0\.0\.1/any/p’ /etc/named.conf
3、在/etc/named.rfc1912.zone中创建区域
- cat >> /etc/named.rfc1912.zones << ENDF \\使用cat追加多行信息到文件中
- zone "rsyslog1.com" IN {
- type master;
- file "rsyslog1.com.zone";
- allow-update { none; };
- };
- ENDF
4、在/var/named/创建zone文件,并设置相应权限(cp -p 可以继承原有权限)
- cp -p /var/named/named.localhost /var/named/rsyslog1.com.zone
- sed -i '/127.0.0.1\|AAAA/d' /var/named/rsyslog1.com.zone //删除不需要的包含127.0.0.1或AAAA的行,其中\|是或的意思
- cat >> /var/named/rsyslog1.com.zone << ENDF //追加A记录
- A 192.168.100.109
- www A 192.168.100.109
- ENDF
5、设置DNS,重启named服务并通过nslookup进行本地解析
- sed -i '1inameserver 192.168.100.109' /etc/resolv.conf //在resolv.conf首行添加nameserver语句,其中1i i是变量当前行上插入一行的意思,1i是在第一行前加入一行
- /etc/rc.d/init.d/named restart
- nslookup www.rsyslog1.com
将以上命令统一放到文件当中就形成了一个简单脚本如下(解释如上)
- #!/bin/bash
- yum install bind bind-chroot -y
- sed -i 's/127\.0\.0\.1/any/' /etc/named.conf
- sed -i 's/::1/any/' /etc/named.conf
- sed -i 's/localhost/any/' /etc/named.conf
- sed -i 's/dnssec/\/\/&/g' /etc/named.conf
- #sed -i -e '/listen-on/d' -e '/allow-query/d' -e '/dnssec/d' /etc/named.conf
- cat >> /etc/named.rfc1912.zones << ENDF
- zone "rsyslog1.com" IN {
- type master;
- file "rsyslog1.com.zone";
- allow-update { none; };
- };
- ENDF
- cp -p /var/named/named.localhost /var/named/rsyslog1.com.zone
- sed -i '/127.0.0.1\|AAAA/d' /var/named/rsyslog1.com.zone
- cat >> /var/named/rsyslog1.com.zone << ENDF
- A 192.168.100.109
- www A 192.168.100.109
- ENDF
- sed -i '1inameserver 192.168.100.109' /etc/resolv.conf
- /etc/rc.d/init.d/named restart
- nslookup www.rsyslog1.com
脚本运行结果如下:
- [root@RHEL6U3-9 named.sh]# ./named1.sh
- ......//省略
- Server: 192.168.100.109
- Address: 192.168.100.109#53
- Name: www.rsyslog1.com
- Address: 192.168.100.109
以上正确解析,说明脚本运行OK
步骤2、将一些常用到的路径以及会变的内容通过变量引用
脚本使用过程中很多部分内容都会在不同的环境下有所变动,为了修改方便,引入变量,每次只需要修改变量的值,脚本中所有引用变量的地方都会自动取定义的变量。(可在vim下使用 :.,$%A%B%g 将光标以下内容中的A全部替换成B)
修改部分:
1、定义安装包变量、配置文件路径变量、域名变量、IP地址变量
脚本修改后如下:
- #!/bin/bash
- PKG="bind bind-chroot" //定义安装的包,不同版本包可能不一样
- M_CONF="/etc/named.conf" //定义主配置文件named.conf路径
- R_CONF="/etc/named.rfc1912.zones" //定义named.rfc1912.zones文件路径
- DOMAIN_N="rsyslog2.com" //定义域名
- MYIP="$(ifconfig eth0 | grep "inet addr:" | awk -F[:" "]+ '{print $4}')" //取eth0的IP地址 –F[:” ”]+表示以:或者空格分隔,’{print $4}’表示打印第四列
- #MYIP="$(ifconfig eth0 | sed –n 's/.*addr:\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).*/\1/p')" //也是取eth0的IP地址,解析如下:
- sed –n ‘s///p’ sed标准写法
- \(\) 保存被匹配的字符 \1 引用第一个保存匹配的字符
- .* 表示所有
- [0-9]\{1,3\} 表示至少1个最多3个数字组合
- \. 表示转义
- #MYIP="$(ifconfig eth0 | grep "inet addr:" | cut -d":" -f2 | cut -d " " -f1)" //取eth0的IP地址,-d: 以:分隔,-d” “ 以空格分隔,-f1 打印:分隔的第一部分
- #MYIP="$(ifconfig eth0 | grep "inet addr" |sed -e 's/.*addr://' -e 's/Bcast.*//') //将IP地址两边的多余部分替换成空即可。
- #MYIP="$("ifconfig eth0 | grep "inet addr" |awk -F: '{print $2}' |awk '{print $1}') //先以:分割过滤,然后以空格分割过滤
- yum install $PKG -y
- sed -i 's/127\.0\.0\.1/any/' $M_CONF
- sed -i 's/::1/any/' $M_CONF
- sed -i 's/localhost/any/' $M_CONF
- sed -i 's/dnssec/\/\/&/' $M_CONF
- cat >> $R_CONF << ENDF
- zone "$DOMAIN_N" IN {
- type master;
- file "$DOMAIN_N.zone";
- allow-update { none; };
- };
- ENDF
- cp -p /var/named/named.localhost /var/named/$DOMAIN_N.zone
- sed -i '/127.0.0.1\|AAAA/d' /var/named/$DOMAIN_N.zone
- cat >> /var/named/$DOMAIN_N.zone << ENDF
- A $MYIP
- www A $MYIP
- ENDF
- sed -i "1inameserver $MYIP" /etc/resolv.conf
- service named restart
- nslookup www.$DOMAIN_N
脚本运行结果:
- [root@RHEL6U3-9 named.sh]# ./named2.sh
- ......
- Server: 192.168.100.109
- Address: 192.168.100.109#53
- Name: www.rsyslog2.com
- Address: 192.168.100.109
- 脚本运行正常
步骤3、将各个部分模块化(函数化)
修改部分:
1、 各模块函数化,后面调用函数即可
2、 安装包函数定义for循环判断是否安装过
3、 创建的域名通过在脚本后面跟上域名进行传递到脚本里面$1里
4、 多行sed命令写入文件中,然后通过-f参数进行引用
5、 函数部分如果比较大可以单独写一个文件,然后在脚本中通过source或者.进行调用。例:source /etc/install.sh或 . /etc/install.sh (以下脚本未举例可自行测试)
脚本修改后如下
- #!/bin/bash
- PKG="bind bind-chroot"
- M_CONF="/etc/named.conf"
- R_CONF="/etc/named.rfc1912.zones"
- DOMAIN_N="$1"
- MYIP="$(ifconfig eth0 | grep "inet addr:" | awk -F[:" "]+ '{print $4}')"
- INSTALL_PKG() //安装包定义一个函数
- {
- # yum install $PKG -y
- for i in $PKG
- do
- rpm -q $i &> /dev/null
- [ $? -ne 0 ] && UNPKG="$UNPKG $i" //将所有检测出来未安装的包保存到变量UPPKG中,也可以保存到文件中,不过变量是临时存储在内存中的,速度要比文件快的多,其次可以减少磁盘I/O性能。
- done
- [ -n "$UNPKG" ] && yum install $UNPKG –y //判断变量UNPKG是否为空,如果不为空就安装里面包含的包
- }
- MAIN_CONF()
- {
- sed -i -f /root/shell/sed.txt $M_CONF //将之前那四行sed内容写到一个文件中,然后通过-f参数进行调用即可,其实四行命令也可以通过-e命令连接,不建议这么操作。
- cat >> $R_CONF << ENDF
- zone "$DOMAIN_N" IN {
- type master;
- file "$DOMAIN_N.zone";
- allow-update { none; };
- };
- ENDF
- cp -p /var/named/named.localhost /var/named/$DOMAIN_N.zone
- sed -i '/127.0.0.1\|AAAA/d' /var/named/$DOMAIN_N.zone
- cat >> /var/named/$DOMAIN_N.zone << ENDF
- A $MYIP
- www A $MYIP
- ENDF
- }
- MYTEST()
- {
- sed -i "1inameserver $MYIP" /etc/resolv.conf
- service named restart
- nslookup www.$DOMAIN_N
- }
- INSTALL_PKG
- MAIN_CONF
- MYTEST
- [root@RHEL6U3-9 shell]# cat /root/shell/sed.txt //引用的四条sed写入的文件格式如下。注意没有’’符号。
- s/127\.0\.0\.1/any
- s/::1/any/
- s/localhost/any/
- s/dnssec/\/\/&/
脚本运行结果如下:
- [root@RHEL6U3-9 named.sh]# ./named3.sh rsyslog3.org
- Stopping named: . [ OK ]
- Starting named: [ OK ]
- Server: 192.168.100.109
- Address: 192.168.100.109#53
- Name: www.rsyslog3.org
- Address: 192.168.100.109
步骤4、完善脚本中存在的各种情况,初步达到生产环境基本要求
修改部分:
1、定义错误函数,方便他人传递参数错误报错。
2、传递参数加上-d 增强书写规范
4、定义测试函数,测试传递参数是否有效
5、定义case语句,内含测试函数,将传递参数一一验证,如果正确报错所有域名参数为后面所用,如果不正确,调用错误函数输出结束。
脚本修改后如下
- #!/bin/bash
- PKG="bind bind-chroot"
- M_CONF="/etc/named.conf"
- R_CONF="/etc/named.rfc1912.zones"
- MYIP="$(ifconfig eth0 | grep "inet addr:" | awk -F[:" "]+ '{print $4}')"
- MYHELP() //定义输出错误方便他人尤其是开发人员正确运行脚本,这也能体现出来一个大牛写脚本是否规范地方,同时也能体现出来团队协作精神,方便他人使用。
- {
- cat << ENDF
- usage: $0 [option] // $0 表示第一个参数 也就是脚本名称
- option:
- -d dn1 dn2 ... :input your domain names
- ENDF
- exit 1
- }
- TEST_D() //定义测试函数,测试输入域名格式是否有效,以下只列出了两种域名格式有效,分别为***.***或者***.***.***
- {
- echo $1 | grep '.\..\|.*\..*\..*' &>/dev/null // \|或的意思
- [ $? -ne 0 ] && MYHELP \\如果执行没有结果或者报错,则显示错误帮助
- }
- INSTALL_PKG()
- {
- # yum install $PKG -y
- for i in $PKG
- do
- rpm -q $i &> /dev/null
- [ $? -ne 0 ] && UNPKG="$UNPKG $i"
- done
- [ -n "$UNPKG" ] && yum install $UNPKG -y
- }
- MAIN_CONF()
- {
- sed -i -f /root/shell/sed.txt $M_CONF
- cat >> $R_CONF << ENDF
- zone "$DOMAIN_N" IN {
- type master;
- file "$DOMAIN_N.zone";
- allow-update { none; };
- };
- ENDF
- cp -p /var/named/named.localhost /var/named/$DOMAIN_N.zone
- sed -i '/127.0.0.1\|AAAA/d' /var/named/$DOMAIN_N.zone
- cat >> /var/named/$DOMAIN_N.zone << ENDF
- A $MYIP
- www A $MYIP
- ENDF
- }
- MYTEST() //由于是可以传递多个域名参数,使用for循环将多个域名进行解析
- {
- sed -i "1inameserver $MYIP" /etc/resolv.conf
- service named restart
- for i in $DOMAIN_N_ALL
- do
- nslookup $i
- done
- }
- [ -z "$1" ] && MYHELP //如果脚本不跟任何传递参数,则退出显示帮助信息
- case $1 in //定义case语句,判断脚本后所有域名是否有效,如果有效全部写入变量DOMAIN_N_ALL中,后面再通过for循环读取变量获取的值,如果无效则退出显示帮助错误,
- -d) //加入 –d 参数后,域名部分从$2开始
- shift 向左移动一位,将$2的值传递给$1,后面依次类推
- TEST_D $1 //判断第一个域名是否有效
- DOMAIN_N_ALL="$1" 将域名保存到变量DOMAIN_N_ALL变量中
- shift 继续左移动,将第二个域名传递给$1
- while [ $# -gt 0 ] //判断位置参数个数是否为0,也就是是否还有域名存在
- do
- TEST_D $1 //如果有继续判断是否有效
- DOMAIN_N_ALL="$DOMAIN_N_ALL $1" //如果有效,将域名追加到DOMAIN_N_ALL变量中,注意中间是有空格的
- shift \\传递之后,位置参数继续左移
- done
- ;;
- *)
- MYHELP
- ;;
- esac
- INSTALL_PKG
- for DOMAIN_N in $DOMAIN_N_ALL //使用for循环添加传递的所有域名
- do
- MAIN_CONF
- done
- MYTEST
脚本执行结果如下:
- [root@RHEL6U3-9 named.sh]# ./named4.sh //不传递任何参数报错
- usage: ./named4.sh [option]
- option:
- -d dn1 dn2 ... :input your domain names
- [root@RHEL6U3-9 named.sh]# ./named4.sh –d //传递参数有问题报错
- usage: ./named4.sh [option]
- option:
- -d dn1 dn2 ... :input your domain names
- [root@RHEL6U3-9 named.sh]# ./named4.sh -d rsyslog1 //传递的参数不符合域名格式报错
- usage: ./named4.sh [option]
- option:
- -d dn1 dn2 ... :input your domain names
- [root@RHEL6U3-9 named.sh]# ./named4.sh -d rsyslog1.net rsyslog2 //传递的第二个域名不符合格式报错
- usage: ./named4.sh [option]
- option:
- -d dn1 dn2 ... :input your domain names
- [root@RHEL6U3-9 named.sh]# ./named4.sh -d rsyslog1.net rsyslog2.net //正确传递两个域名,后面还可以跟域名
- Stopping named: . [ OK ]
- Starting named: [ OK ]
- Server: 192.168.100.109
- Address: 192.168.100.109#53
- Name: rsyslog1.net
- Address: 192.168.100.109
- Server: 192.168.100.109
- Address: 192.168.100.109#53
- Name: rsyslog2.net
- Address: 192.168.100.109
步骤5、继续完善脚本中存在的各种情况,最终达到生产环境要求
1、 加复杂文件锁,防止脚本被重复执行或者系统宕机再次被执行
2、 ……………..
待续.......................
----------------技术源于分享,懂得分享的人才能够真正的体会到技术的快乐!