- Linux编程篇
4.1 Shell编程简介
shell是操作系统的最外层。shell合并编程语言以控制进程和文件,以及启动和控制其它程序。shell 通过提示您输入,向操作系统解释该输入,然后处理来自操作系统的任何结果输出来管理您与操作系统之间的交互。
Shell是用户与Linux操作系统之间沟通的桥梁。用户可以输入命令执行,又可以利用 Shell脚本编程去运行。随着Linux企业应用越来越多,维护Linux日常工作频繁,所以如果单靠手工去敲打命令是非常困难的,所以学会熟练使用SHELL编程是每个Linux SA必备的功课。
Linux Shell种类非常多,常见的有:Bourne Shell(/usr/bin/sh或/bin/sh)、Bourne Again Shell(/bin/bash)、C Shell(/usr/bin/csh)、K Shell(/usr/bin/ksh)、Shell for Root(/sbin/sh)等。不同的Shell语言的语法有所不同,所以不能交换使用。
最常用的shell是Bash,也就是Bourne Again Shell,由于易用和免费,Bash在日常工作中被广泛使用,也是大多数Linux系统默认的Shell。接下来我们来写一个简单的shell脚本。(shell脚本一般文件名以.sh结尾,同时文件第一行定义该脚本为shell脚本)
vi first_shell.sh
#!/bin/bash
#This is my First shell
echo “Hello World !”
这就是我们的第一个脚本,是不是很简单呢,注解如下:
#!/bin/bash //表示定义该脚本是一个shell脚本(固定格式)。
#This is my First shell //这里的#号属于注解,没有任何的意义,SHELL不会解析它。
echo “Hello World !” //shell脚本主命令,我们执行这个脚本讲看到: Hello World ! 信息。
脚本编写完毕,如何来执行呢,首先执行shell脚本需要执行权限,赋予执行权限:
chmod o+x first_shell.sh
然后./first_shell.sh
执行即可;也可以直接使用命令执行: /bin/sh first_shell.sh
,显示效果一样。
4.2 Shell变量设置
Shell编程语言是非类型的解释型语言,不像C++/JAVA语言编程时需要事先声明变量,SHELL给一个变量赋值,实际上就是定义了变量,在Linux支持的所有shell中,都可以用赋值符号(=)为变量赋值。
SHELL变量可分为两类:局部变量和环境变量。局部变量只在创建它们的shell脚本中使用。而环境变量则可以在创建它们的shell及其派生出来的任意子进程中使用。有些变量是用户创建的,其他的则是专用shell变量。
例如在脚本里面定义A=123 ,定义这样一个变量,前面变量名,后面是变量的值。
引用变量可以使用$A,把变量放在脚本里面会出现什么样的效果呢?如下:
#!/bin/bash
#Author wugk 2014-06-10
A=123
echo “Printf variables equal is $A”
执行脚本:sh test.sh
,结果将会显示:
Printf variables equal is 123
简单的理解变量,相当于定义一个别名-名称,引用的时候加上$符号就可以了。
例如定义变量 name=wuguangke
执行echo $name
将会显示wuguangke
SHELL常见的系统变量解析:
$0 当前程序的名称
$n 当前程序的第n个参数,n=1,2,…9
$* 当前程序的所有参数(不包括程序本身)
$# 当前程序的参数个数(不包括程序本身)
$? 命令或程序执行完后的状态,一般返回0表示执行成功。
$UID 当前用户的ID
$PWD 当前所在的目录
4.3 Shell流程控制语句
在Linux Shell编程中,if、for、while、case等条件流程控制语句用的非常多,把这些学好,对提升脚本的功力有非常大的帮助。下面将逐个来讲解具体的用法:
If条件判断语句
If (表达式) #if ( Variable in Array )
语句1
else
语句2
fi
案例一,测试数字大小
#!/bin/sh
NUM=100
if (( $NUM > 4 )) ;then
echo “this num is $NUM greater 4 !”
fi
案例二,测试目录是否存在,不存在则新建(注意,中括号之间必须要空格)
#!/bin/sh
#judge dir exist
if [ ! -d /data/20140515 ];then
mkdir -p /data/20140515
else
echo “This DIR is exist,Please exit …..”
fi
逻辑运算符解析:
-f 判断文件是否存在 eg: if [ -f filename ]
-d 判断目录是否存在 eg: if [ -d dir ]
-eq 等于 应用于:整型比较
-ne 不等于 应用于:整型比较
-lt 小于 应用于:整型比较
-gt 大于 应用于:整型比较
-le 小于或等于 应用于:整型比较
-ge 大于或等于 应用于:整型比较
-a 双方都成立(and) 逻辑表达式 –a 逻辑表达式
-o 单方成立(or) 逻辑表达式 –o 逻辑表达式
-z 空字符串
案例三,多个条件测试判断
#!/bin/sh
scores=80;
if [[ $scores -gt 85 ]]; then
echo "very good!";
elif [[ $scores -gt 75 ]]; then
echo "good!";
elif [[ $scores -gt 60 ]]; then
echo "pass!";
else
echo "no pass!";
fi;
循环语句for
For 变量 in 字符串
do
语句1
done
案例一,打印seq多个数
#!/bin/sh
for i in `seq 15`
do
echo “NUM is $i”
done
案例二,找到相关log,然后批量打包
#!/bin/sh
for i in `find /var/log -name “*.log”`
do
tar –czf 2014log.tgz $i
done
循环语句while
while 条件语句
do
语句1
done
案例一,while条件判断数字
#!/bin/sh
i=1;
while [[ $i -lt 10 ]];do
echo $i;
((i++));
done;
案例二,while逐行读取某个文件
#!/bin/sh
while read line
do
echo $line;
done < /etc/hosts
Until循环语句
until 条件
do
action
done
直到满足条件,才退出。否则执行action。
案例一,条件判断数字
#!/bin/sh
a=10;
until [[ $a -lt 0 ]];do
echo $a;
((a--));
done;
Case选择语句
case $arg in
pattern1)
语句1
;;
pattern2)
语句2
;;
*)
语句3
;;
esac
案例一,创建选择参数脚本
#!/bin/sh
case $1 in
monitor_log)
monitor_log
;;
archive_log)
archive_log
;;
* )
echo "Usage:{$0 monitor_log | archive_log |help }"
;;
esac
select选择语句
#!/bin/sh
PS3="What you like most of the open source system?"
select i in CentOS RedHat Ubuntu
do
echo "Your Select System: "$i
done
4.4 Shell脚本案例
a) 自动删除test.txt文件脚本
脚本的功能实现从/root/目录cp拷贝test.txt到/tmp目录,并且在/tmp目录创建一个目录abc,并且删除原/root/下test.txt。
首先命名脚本名称为auto_cp.sh(名称可以自己定义),内容如下:
#!/bin/bash
#This is First shell for auto cp Files
#定义文件和目录变量
FILES=/root/test.txt
DIR=/tmp
cp $FILES $DIR
cd $DIR ; mkdir –p abc
rm -rf $FILES
echo “The Shell Scripts exec successfully !”
“自动CP并删除文件”的脚本编写完毕,保存退出即可。
b) 自动备份Mysql数据库脚本
#!/bin/sh
#auto backup mysql
#wugk 2012-12-12
#Define PATH定义变量
BAKDIR=/data/backup/mysql/`date +%Y-%m-%d`
MYSQLDB=webapp
MYSQLPW=backup
MYSQLUSR=backup
#must use root user run scripts 必须使用root用户运行,$UID为系统变量
if
[ $UID -ne 0 ];then
echo This script must use the root user ! ! !
sleep 2
exit 0
fi
#Define DIR and mkdir DIR 判断目录是否存在,不存在则新建
if
[ ! -d $BAKDIR ];then
mkdir -p $BAKDIR
else
echo This is $BAKDIR exists....
fi
#Use mysqldump backup mysql 使用mysqldump备份数据库
/usr/bin/mysqldump -u$MYSQLUSR -p$MYSQLPW -d $MYSQLDB >$BAKDIR/webapp_db.sql
echo "The mysql backup successfully "
c) 自动打包tar目录下log文件脚本
#!/bin/sh
#auto tar czf shell to Files
#Author wugk 2014-05-15
SRC_DIR=/opt/
DES_DIR=/opt/backup/`date +%Y%m%d`
If
[ ! –d $DES_DIR ];then
Mkdir –p $DES_DIR
fi
for i in `find $SRC_DIR -name “*.log”`
do
tar czf $i.tgz $i
done
echo “The scripts exec end, Files tar successfully !”
d) 自动拒绝恶意IP脚本
#!/bin/sh
#auto drop ssh failed IP address
#wugk 2013-1-2
#定义变量
SEC_FILE=/var/log/secure
#如下为截取secure文件恶意ip 远程登录22端口,大于等于4次就写入防火墙,禁止以后再登录服务器的22端口
IP_ADDR=`tail -n 1000 /var/log/secure |grep "Failed password"| egrep -o "([0-9]{1,3}\.){3}[0-9]{1,3}" | sort -nr | uniq -c |awk ' $1>=4 {print $2}'`
IPTABLE_CONF=/etc/sysconfig/iptables
echo
cat <<EOF
++++++++++++++welcome to use ssh login drop failed ip+++++++++++++++++
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
++++++++++++++++------------------------------------++++++++++++++++++
EOF
echo -n "请等待5秒后开始执行 "
for ((j=0;j<=4;j++)) ;do echo -n "----------";sleep 1 ;done
echo
for i in `echo $IP_ADDR`
do
#查看iptables配置文件是否含有提取的IP信息
cat $IPTABLE_CONF |grep $i >/dev/null
if
[ $? -ne 0 ];then
#判断iptables配置文件里面是否存在已拒绝的ip,如何不存在就不再添加相应条目
sed -i "/lo/a -A INPUT -s $i -m state --state NEW -m tcp -p tcp --dport 22 -j DROP" $IPTABLE_CONF
else
#如何存在的话,就打印提示信息即可
echo "This is $i is exist in iptables,please exit ......"
fi
done
#最后重启iptables生效
/etc/init.d/iptables restart
4.5 Shell数组编程
数组,就是相同数据类型的元素按一定顺序排列的集合,就是把有限个类型相同的变量用一个名字命名,然后用编号区分他们的变量的集合,这个名字成为数组名,编号成为下标。
今天这里我们来探讨一维数组的定义、统计、引用和删除等操作。首先来定义一个一维数组:
A=( test1 test2 test3 ) ,定义数组一般以括号的方式来定义,数组的值可以随机定义。如何来引用呢?
echo ${A[0]},代表引用第一个数组变量,结果会显示test1,数组引用从0开始,代表第一个数组,依次类推。
echo ${A[1]},代表引用第二个数组变量,结果会显示test2,数组引用也是从0开始计算的。
如何显示该数组所有参数呢?echo ${A[@]} 将显示所有参数test1 test2 test3 。
如何显示该数组参数个数呢?echo ${#A[@]} 将显示该数组的参数个数3。
如果替换某个数组呢?例如替换第二个test2数组为test5:echo ${A[@]/test2/test5}
如何删除一个数组呢?例如删除test3数组命令为:unset A[2] ;echo ${A[@]}查看效果。
那输入如何在编程来使用呢?请看下面例子:
#!/bin/sh
#Auto Make KVM Virtualization
#Auto config bond scripts
eth_bond()
{
NETWORK=(
HWADDR=`ifconfig eth0 |egrep "HWaddr|Bcast" |tr "\n" " "|awk '{print $5,$7,$NF}'|sed -e 's/addr://g' -e 's/Mask://g'|awk '{print $1}'`
IPADDR=`ifconfig eth0 |egrep "HWaddr|Bcast" |tr "\n" " "|awk '{print $5,$7,$NF}'|sed -e 's/addr://g' -e 's/Mask://g'|awk '{print $2}'`
NETMASK=`ifconfig eth0 |egrep "HWaddr|Bcast" |tr "\n" " "|awk '{print $5,$7,$NF}'|sed -e 's/addr://g' -e 's/Mask://g'|awk '{print $3}'`
GATEWAY=`route -n|grep "UG"|awk '{print $2}'`
)
cat >ifcfg-bond0<<EOF
DEVICE=bond0
BOOTPROTO=static
${NETWORK[1]}
${NETWORK[2]}
${NETWORK[3]}
ONBOOT=yes
TYPE=Ethernet
NM_CONTROLLED=no
EOF
如上脚本为定义三个数组变量,然后分别来引用,这样让脚本可读性更强,更整洁。关于数组就简单的介绍到这里。