简介
Shell脚本与Windows/Dos下的批处理相似,也就是用各类命令预先放入到一个文件中,方便一次性执行的一个程序文件,主要是方便管理员进行设置或者管理用的。但是它比Windows下的批处理更强大,比用其他编程程序编辑的程序效率更高,它使用了Linux/Unix下的命令
变量
变量类型
本地变量:当前用户进程
环境变量:当前用户进程和子进程(env/set)
全局变量:所有用户程序都能调用
系统变量:内置bash变量
$?:上一条命令执行返回状态;0表示执行正常,非0表示执行不正常
$#:表示脚本接的参数个数
$*:表示脚本后面所有参数
$@:表示脚本后面所有参数
$0:运行脚本的名称
${1}-${9}:表示脚本后面的位置参数
$$:当前所在进程的进程号
$!:最后一个放在后台运行的进程号
!$:调用最后一条命令的参数
数组
普通数组(整数作为下标)
分别定义:array[0]=v1 array[1]=v2 ...
连续定义:array=(var1 var2 ... varn)
array=(`cat /etc/passwd`)通过空格分隔
查看单个元素:${array[0]}
查看所有元素:
${array[*]}
${array[@]}
截取部分元素:${array[*]:1:3}
查看元素个数:${#array[*]}
获取元素下标:
${!array[*]}
${!array[@]}
关联数组(可使用字符串作为下标,先声明在定义)
定义:declare -A array
查看关联数组 declare -A
分别定义:array[a]=v1 array[b]=v2 ...
连续定义:array=([name1]=var1 [name2]=var2 ... [namen]=varn)
查看单个元素:${array[a]}
查看所有元素:
${array[*]}
${array[@]}
截取部分元素:${array[*]:1:3}
查看元素个数:${#array[*]}
获取元素下标:
${!array[*]}
${!array[@]}
运算(默认为0):
let array[name1]++
let array[name1]+=num
清空数组
unset array
用法
魔法字节(通配)
#!/bin/env bash
查看运行过程
[root@localhost ~]# bash -x my.sh
查看进程ID
[root@localhost ~]# pgrep 进程名称 &> /dev/null
查看语法问题
[root@localhost ~]# bash -n my.sh
date工具
前一分钟(日周月年)
date -d '-1 minute'
后一分钟
date -d '1 minute'
设置格式
date +%d/%b%Y:%H%M
语法
调用变量某部分
A=123456
echo ${A:2:3}=>345
删除字符串的一部分
从后往前删
${array%xxx}
从文件读取变量
read -p "Input:" name < filename
定义有类型变量,对变量做限制
declare [options] 变量名=变量值
常用选项
-i:声明一个整型
-r:声明一个readonly变量,该变量的值无法改变,并且不能为unset
-a:声明一个数组
-x:声明环境变量
四则运算(仅整数)
$((1+1))
$[10-5]
expr 1 + 1(不可求幂)
n=1;let n=n+1;echo $n(需先定义变量)
shift
使位置参数左移(默认1位),也可以设定为多位
取出当前文件的目录和名称
dirname $A
basename $A
shift
#输入参数one two three four five six
#shift就是右移拿掉多少个参数的意思
echo "The script name is ==> ${0} "
echo "Total parameter number is ==> $# "
echo "Your whole parameter is ==> '$@' "
shift #进行一个变量的shift
echo "Total parameter number is ==> $# "
echo "Your whole parameter is ==> '$@' "
shift 3 #第二次进行三个变量的shift
echo "Total parameter number is ==> $# "
echo "Your whole parameter is ==> '$@' "
变量内容删除和替换
url=www.haha.com
获取长度:${$url}
左往右去掉一个key:${url#*.}=>haha.com
左往右去掉最大程度去掉:${url##*.}=>com
右往左去掉一个key:${url%*.}=>www.haha
右往左去掉最大程度去掉:${url%%*.}=>www
并发执行
用{}&包住函数体,加入wait关键字
实现输出
cat << -EOF
xx
EOF
随机数(0-32767)
$RANDOM
产生0-n-1之间随机数
$[$RANDOM%n]
产生10-99
$[$RANDOM%90+10]
expect(实现自动应答)
#位置参数定义变量
set ip xxx
set pass xxx
#开启一个程序
spawn ssh root@$ip
#捕获相应内容
#exp_continue参数表示可忽略此捕获
expect {
"(yes/no)?" { send "yes\r";exp_continue }
"password:" { send "$pass\r";exp_continue }
}
#进行操作
expect "#"
send "rm -rf /tmp/*\r"
send "hostname\r"
send "date +%F\r"
send "touch file{1..3}\r"
send "ls /tmp/\r"
expect eof
条件判断
语法
test 条件
[ 条件 ]
[[ 条件 ]]
类C风格
数值比较:(( 条件 ))
[]和[[]]区别:
判断空字符串时,[]需要对变量加"";[[]]不需要
&&和||时,[]要分开多个条件,[[]]不需要
流程控制
if结构
语法
if [[ condition1 ]];then
command1
elif [[ condition2 ]];then
command2
else
command3
fi
for结构
语法
带列表:
for variable in {list}
do
command
done
不带列表(由用户输入的参数决定,$1$2):
for variable [in "$@"]
类C风格:
for (( expr1;expr2;expr3 ))
while结构
语法
while 表达式
do
command
done
until结构
语法
until expression
do
command
done
case语句
语法
case var in
pattern 1)
command
;;
pattern 2)
command
;;
*)#相当于default
command
;;
esac
select循环(方便输出菜单,无限循环)
语法
#定义提示符,$PS1,$PS2,$PS3
PS3="Your choice is:"
select choice in xxx xxx xxx
do
case "$choice" in
xxx)
esac
done
函数
语法
[function] 函数名(){
函数体
打印传给函数的第一个参数:echo $1
}
调用
终端临时调用:source function.sh;function_name
环境变量:函数写入/etc/bashrc(用户在家目录.bashrc)
脚本中调用:function_name(脚本中定义|source function.sh)
应用
需求:随机产生1000个139开头的电话号码,并抽取五位幸运观众
n1=0
n2=0
n3=0
n4=0
n5=0
n6=0
n7=0
n8=0
file=/tmp/phonenum.txt
for ((i=1;i<=1000;i++))
do
for ((j=1;j<=8;j++))
do
let n$j=$[$RANDOM%10]
done
echo "139$n1$n2$n3$n4$n5$n6$n7$n8" >> $file
done
#!/bin/env bash
#抽取五个幸运观众
phone=/tmp/phonenum.txt
for ((i=1;i<=5;i++))
do
line=`wc -l $phone|cut -d ' ' -f1`
lucky_lines=$[$RANDOM%$line+1]
#取出号码
luck_num=`head -$lucky_lines $phone |tail -1`
#替换显示到屏幕
echo "139****${luck_num:7:4}"
echo $luck_num >> /tmp/luck.txt
#删除已抽取的号码,sed用shell变量要用双引号
sed -i "/$luck_num/d" $phone
done
需求:保留最近3天的日志
#!/bin/bash
#实现一:
#定期删除目录下修改时间大于7天的文件,使用绝对路径
#脚本加上i权限防止误操作
#方法一:
`find /path -mtime +7 -exec rm -r {} \;`
#方法二:
`find /path -mtime +7 |xargs rm -rf `
#实现二:
#一直保留前两个工作日的备份文件,使用绝对路径
#方法一:
`ls -t /path/*.tar.gz | awk 'NR>2' |xargs rm -rf `
#方法二:
`ls -t /path/*.tar.gz|awk 'NR>2 {print "rm -f "$0""}'|bash`
需求:统计网站连接状态
#!/bin/bash
declare -A state_array
states=`ss -ant|grep :80 |cut -d ' ' -f1`
for i in $states
do
let state_array[$i]++
done
for j in ${!state_array[*]}
do
echo $j:${state_array[$j]}
done
需求:脚本查看系统性能
#!/bin/bash
PS3="Your choice is:"
#判断系统
os_check(){
if [ -e /etc/redhat-release ];then
REDHAT=`cat /etc/redhat-release|cut -d ' ' -f1`
else
DEBIAN=`cat /etc/issue|cut -d ' ' -f1`
fi
if [ "$REDHAT" == "Centos" -o "$REDHAT" == "Red" ];then
P_M=yum
elif [ "$DEBIAN" == "Ubuntu" -o "$DEBIAN" == "ubuntu" ];then
P_M=apt-get
else
echo "Operating system does not support."
exit 1
fi
}
if [ $LOGNAME != root ];then
echo "Please use the root account operation."
exit 1
fi
instll_software(){
if ! which $1 &> /dev/null;then
echo $1 "command not found,now the install."
sleep 1
os_check
$P_M install $2 -y
echo "--------------------"
fi
}
while true
do
select input in cpu_load disk_load disk_use disk_inode mem_use tcp_status cpu_top10 mem_top10 traffic quit
do
case $input in
cpu_load)
#CPU利用率和负载
echo "-------------------------"
i=1
while [[ $i -le 3 ]]
do
#定义颜色
echo -e "\033[32m 参考值${i}\033[0m"
UTIL=`vmstat|awk '{if(NR==3)print 100-$15"%"}'`
USER=`vmstat|awk '{if(NR==3)print $13"%"}'`
SYS=`vmstat|awk '{if(NR==3)print $14"%"}'`
IOWAIT=`vmstat|awk '{if(NR==3)print $16"%"}'`
echo "Util:$UTIL"
echo "User use:$USER"
echo "System use:$SYS"
echo "I/O wait:$IOWAIT"
let i++
sleep 1
done
echo "-------------------------"
break
;;
disk_load)
#硬盘I/O负载
echo "-------------------------"
i=1
while [[ $i -le 3 ]]
do
echo -e "\033[32m 参考值${i}\033[0m"
UTIL=`iostat -x -k|awk '/^[v|s]da/{print $1,$NF"%"}'`
READ=`iostat -x -k|awk '/^[v|s]da/{print $1,$4"KB"}'`
WRITE=`iostat -x -k|awk '/^[v|s]da/{print $1,$5KB"%"}'`
IOWAIT=`vmstat|awk '{if(NR==3)print $16"%"}'`
echo "Util:$UTIL"
echo "Read:$READ"
echo "Write:$WRITE"
echo "I/O wait:$IOWAIT"
let i++
sleep 1
done
echo "-------------------------"
break
;;
disk_use)
echo "------------------"
#硬盘利用率
DISK_TOTAL=`fdisk -l|awk '/dev\/sda/&& /^Disk.*字节/{print $2"GB"}'`
USE_RATE=`df |awk '/\/dev\/mapper/{print $5}'`
for i in $USE_RATE
do
flag=`awk -v num1="$i" -v num2=90% 'BEGIN{print (num1<=num2)?"0":"1" }'`
if [ $flag -eq 1 ];then
PART=`df -h|awk '{if(int($5)=='''$i''') print $6}'`
echo "$PART=${i}%"
fi
done
echo "Disk total:${DISK_TOTAL}"
echo "Use rate:${USE_RATE}"
echo "--------------------"
break
;;
disk_inode)
echo "------------------"
#硬盘inode利用率
USE_RATE=`df -i |awk '/\/dev\/mapper/{print $5}'`
for i in $USE_RATE
do
flag=`awk -v num1="$i" -v num2=90% 'BEGIN{print (num1<=num2)?"0":"1" }'`
if [ $flag -eq 1 ];then
PART=`df -h|awk '{if(int($5)=='''$i''') print $6}'`
echo "$PART=${i}%"
else
echo "UseRate:"${USE_RATE}
echo "Inode use rate no than 90% of the partition."
fi
done
echo "------------------"
break
;;
mem_use)
echo "------------------"
MEM_TOTAL=`free -m|awk '{if(NR==2)printf"%.1f",$2/1024}END{print"G"}'`
USE=`free -m|awk '{if(NR==2)printf"%.1f",$3/1024}END{print"G"}'`
FREE=`free -m|awk '{if(NR==2)printf"%.1f",$4/1024}END{print"G"}'`
CACHE=`free -m|awk '{if(NR==2)printf"%.1f",$6/1024}END{print"G"}'`
echo "Total:${MEM_TOTAL}"
echo "Use:${USE}"
echo "Free:${FREE}"
echo "Cache:${CACHE}"
echo "------------------"
break
;;
tcp_status)
#网络连接状态
echo "------------------"
COUNT=`ss -ant|awk '!/State/{status[$1]++}END{for (i in status)print i,status[i]}'`
echo "Tcp connect status:\n$COUNT"
echo "------------------"
break
;;
cpu_top10)
#CPU占用率前10进程
echo "------------------"
CPU=`ps -axu|awk '{if($3>0.1) print "PID: "$2" CPU: "$3"% --> "$NF"\n"}'|sort -k4 -nr|head -10`
num=0
for i in $CPU
do
echo -n "$i"
let num++
if [ $num -eq 6 ];then
echo
let num=0
fi
done
echo "------------------"
break
;;
mem_top10)
#内存占用前10进程
echo "------------------"
MEM=`ps -axu|awk '{if($4>0.1){print "PID: "$2" Memory: "$4"% --> "$NF""}}'|sort -k4 -nr|head -10`
num=0
for i in $MEM
do
echo -n "$i"
let num++
if [ $num -eq 6 ];then
echo
let num=0
fi
done
echo "------------------"
break
;;
traffic)
#网络流量
while true
do
read -p "Input network cat name (ens[33/37]):" ens
#判断是否存在
if [ `ifconfig |grep -c "\<$ens\>"` -eq 1 ];then
break
else
echo "Input error,please input again."
fi
done
echo "---------------------------"
OLD_RX=`ifconfig ens33 |awk '{if(NR==5)print $5}'`
OLD_TX=`ifconfig ens33 |awk '{if(NR==7)print $5}'`
sleep 1
NEW_RX=`ifconfig ens33 |awk '{if(NR==5)print $5}'`
NEW_TX=`ifconfig ens33 |awk '{if(NR==7)print $5}'`
IN=`awk 'BEGIN{printf "%.1f\n",'$((${NEW_RX}-${OLD_RX}))'/1024/1024}'`
OUT=`awk 'BEGIN{printf "%.1f\n",'$((${NEW_TX}-${OLD_TX}))'/1024/1024}'`
echo "IN:${IN}MB/s OUT:${OUT}MB/s"
echo "---------------------------"
break
;;
quit)
exit
;;
*)
echo "---------------------------"
echo "Input number"
break
echo "---------------------------"
;;
esac
done
done