简介
Shell脚本与Windows/Dos下的批处理相似,也就是用各类命令预先放入到一个文件中,方便一次性执行的一个程序文件,主要是方便管理员进行设置或者管理用的。但是它比Windows下的批处理更强大,比用其他编程程序编辑的程序效率更高,它使用了Linux/Unix下的命令
变量
变量类型本地变量:当前用户进程环境变量:当前用户进程和子进程(env/set)全局变量:所有用户程序都能调用系统变量:内置bash变量$?:上一条命令执行返回状态;0表示执行正常,非0表示执行不正常$#:表示脚本接的参数个数$*:表示脚本后面所有参数$@:表示脚本后面所有参数$0:运行脚本的名称${1}-${9}:表示脚本后面的位置参数$$:当前所在进程的进程号$!:最后一个放在后台运行的进程号!$:调用最后一条命令的参数$USER:显示当前用户信息$UID:显示当前用户uid$HOME:显示当前用户家目录只读变量readonly variable删除变量unset variable定义有类型变量,对变量做限制declare [options] 变量名=变量值常用选项-i:声明一个整型-r:声明一个readonly变量,该变量的值无法改变,并且不能为unset-a:声明一个数组-x:声明环境变量
字符串
#shell编程中字符串可以用单引号,也可以用双引号,也可以不用引号#单引号字符串的限制:1)单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;2)单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用用法#截取字符串A=123456echo ${A:2:3}=>345#删除字符串的一部分(从后往前删)${string%substring}#拼接字符串string=\"var1\"$var2\"var3\"#获取字符串长度${#string}#查找子字符串(找i或o的位置,以先出现的字符为准)echo `expr index $string io`#字符串内容删除和替换url=www.haha.com左往右去掉一个key:${url#*.}=>haha.com左往右去掉最大程度去掉:${url##*.}=>com右往左去掉一个key:${url%*.}=>www.haha右往左去掉最大程度去掉:${url%%*.}=>www#字符串运算=:检测两个字符串是否相等,相等返回true!=:检测两个字符串是否相等,不相等返回true-z:检测字符串长度是否为0,为0返回true-n:检测字符串长度是否不为0,不为0返回true$:检测字符串是否为空,不为空返回true#字符串比较\\>:检查字符串大小关系,左边大于右边则为true\\<:检查字符串大小关系,左边小于右边则为true
数组
普通数组(整数作为下标)分别定义: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
语法
read方法
#从文件读取变量read -p \"Input:\" name < filename#隐藏读取变量read -s -p \"Please enter your password\" pass#设置读取超时read -t -p \"Please Input data:\" data#设置读取输入次数(次数为n后的数字)read -n1 -p \"Please Input data\" data#读取多个参数,空格隔开read -p \"Input your name(firstname lastname)\" first last
按行读取文件内容
cat test|while read linedoxxdone
算术运算(仅整数)
$((1+1))$[10-5]expr 1 + 1(不可求幂)n=1;let n=n+1;echo $n(需先定义变量)#比较$a == $b:相等$a != $b:不相等
关系运算符
-eq:检测两个数是否相等,相等返回true-ne:检测两个数是否不相等,不相等返回true-gt:检测左边的数是否大于右边,如果是,则返回true-lt:检测左边的数是否小于右边,如果是,则返回true-ge:检测左边的数是否大于等于右边,如果是,则返回true-le:检测左边的数是否小于等于右边,如果是,则返回true
布尔算术
!:非运算,表达式为true则返回false,否则返回true-o:或运算,有一个表达式为true则返回 true-a:与运算,两个表达式都为true才返回true
文件比较
-e:判断对象是否存在-d:判断对象是否存在,并且为目录-f:判断对象是否存在,并且为常规文件-L:判断对象是否存在,并且为符号链接-h:判断对象是否存在,并且为软链接-s:判断对象是否存在,并且长度不为0-r:判断对象是否存在,并且可读-w:判断对象是否存在,并且可写-x:判断对象是否存在,并且可执行-O:判断对象是否存在,并且属于当前用户-G:判断对象是否存在,并且属于当前用户组-nt:判断file1是否比file2新-ot:判断file1是否比file2旧
取出当前文件的名称
basename $0
shift
#使位置参数左移(默认1位),也可以设定为多位#输入参数one two three four five six#shift就是右移拿掉多少个参数的意思echo \"The script name is ==> ${0} \"echo \"Total parameter number is ==> $# \"echo \"Your whole parameter is ==> \'$@\' \"shift #进行一个变量的shiftecho \"Total parameter number is ==> $# \"echo \"Your whole parameter is ==> \'$@\' \"shift 3 #第二次进行三个变量的shiftecho \"Total parameter number is ==> $# \"echo \"Your whole parameter is ==> \'$@\' \"
并发执行
用{}&包住函数体,加入wait关键字
实现输出
cat << -EOFxxEOF
随机数(0-32767)
$RANDOM产生0-n-1之间随机数$[$RANDOM%n]产生10-99$[$RANDOM%90+10]
expect(实现自动应答)
#位置参数定义变量set ip xxxset 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
getopt用法
#用法:getopts 选项 参数注:选项后加:则改选项必须带参数,若选项后不带:则可以不带参数#实例#!/bin/bashwhile getopts a:b:c:def optdocase \"$opt\" ina)echo \"Found the -a option\";;b)echo \"Found the -b option\";;c)echo \"Found the -c option\";;d)echo \"Found the -d option\";;e)echo \"Found the -e option\";;f)echo \"Found the -f option\";;*)echo \"Unknown option :$opt\";;esacdone
捕捉信号
用法trap \"命令(可选)\" 信号(多个用空格相隔)#捕捉ctrl-c信号并提示trap \"echo \'Sorry! I have trapped Ctrl-C\'\" SIGINT SIGTERM#捕捉脚本退出并提示trap \"echo bye\" EXIT#移除捕捉trap -EXIT
条件判断
语法test 条件[ 条件 ][[ 条件 ]]类C风格数值比较:(( 条件 ))[]和[[]]区别:判断空字符串时,[]需要对变量加\"\";[[]]不需要&&和||时,[]要分开多个条件,[[]]不需要
流程控制
if结构
语法if [[ condition1 ]];thencommand1elif [[ condition2 ]];thencommand2elsecommand3fi
for结构
语法带列表:for variable in $listdocommanddone不带列表(由用户输入的参数决定,$1$2):for variable in \"$@\"类C风格:for (( expr1;expr2;expr3 ))
while结构
语法while 表达式docommanddone
until结构
语法until expressiondocommanddone
case语句
语法case var inpattern 1)command;;pattern 2)command;;*)#相当于defaultcommand;;esac
select循环(方便输出菜单,无限循环)
语法#定义提示符,$PS1,$PS2,$PS3PS3=\"Your choice is:\"select choice in xxx xxx xxxdocase \"$choice\" inxxx)esacdone
函数
语法[function] 函数名(){函数体打印传给函数的第一个参数:echo $1}调用终端临时调用:source function.sh;function_name环境变量:函数写入/etc/bashrc(用户在家目录.bashrc)脚本中调用:function_name(脚本中定义|source function.sh)返回值函数最后一条命令执行成功默认返回状态码为0函数最后一条命令执行不成功则返回状态码为非0
函数返回
echo输出内容function f{read -p \"Enter a value:\" valueecho $[ $value*2 ]}result=`db1`echo \"The new value is $result\"return返回值function f{read -p \"Enter a value:\" valueecho \"doubling the value\"return $[ $value * 2 ]}db1echo \"The new value is $?\"
函数传参
function f{# $1和$2 不能从命令行中传递,只能在调用函数时手动进行传递echo $[ $1 * $2 ]}var1=3var2=5value=`f $var1 $var2`echo \"The result is $value\"
向函数传递数组
function f {echo \"The parameters are : $@\"#函数只会读取数组变量的第一个值thisarray=$1echo \"The received array is ${thisarray[*]}\"newarray=(`echo \"$@\"`)echo \"The new array value is : ${newarray[*]}\"}myarray=(1 2 3 4 5)echo \"The original array is : ${myarray[*]}\"#将数组变量当成一个函数参数,函数只会去函数变量第一个值#f $myarray#向函数传递数组的所有元素函数才可读取整个数组f ${myarray[*]}
应用
需求:随机产生1000个139开头的电话号码,并抽取五位幸运观众
n1=0n2=0n3=0n4=0n5=0n6=0n7=0n8=0file=/tmp/phonenum.txtfor ((i=1;i<=1000;i++))dofor ((j=1;j<=8;j++))dolet n$j=$[$RANDOM%10]doneecho \"139$n1$n2$n3$n4$n5$n6$n7$n8\" >> $filedone
#!/bin/env bash#抽取五个幸运观众phone=/tmp/phonenum.txtfor ((i=1;i<=5;i++))doline=`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\" $phonedone
需求:保留最近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/bashdeclare -A state_arraystates=`ss -ant|grep :80 |cut -d \' \' -f1`for i in $statesdolet state_array[$i]++donefor j in ${!state_array[*]}doecho $j:${state_array[$j]}done
需求:脚本查看系统性能
#!/bin/bashPS3=\"Your choice is:\"#判断系统os_check(){if [ -e /etc/redhat-release ];thenREDHAT=`cat /etc/redhat-release|cut -d \' \' -f1`elseDEBIAN=`cat /etc/issue|cut -d \' \' -f1`fiif [ \"$REDHAT\" == \"Centos\" -o \"$REDHAT\" == \"Red\" ];thenP_M=yumelif [ \"$DEBIAN\" == \"Ubuntu\" -o \"$DEBIAN\" == \"ubuntu\" ];thenP_M=apt-getelseecho \"Operating system does not support.\"exit 1fi}if [ $LOGNAME != root ];thenecho \"Please use the root account operation.\"exit 1fiinstll_software(){if ! which $1 &> /dev/null;thenecho $1 \"command not found,now the install.\"sleep 1os_check$P_M install $2 -yecho \"--------------------\"fi}while truedoselect input in cpu_load disk_load disk_use disk_inode mem_use tcp_status cpu_top10 mem_top10 traffic quitdocase $input incpu_load)#CPU利用率和负载echo \"-------------------------\"i=1while [[ $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 1doneecho \"-------------------------\"break;;disk_load)#硬盘I/O负载echo \"-------------------------\"i=1while [[ $i -le 3 ]]doecho -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 1doneecho \"-------------------------\"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_RATEdoflag=`awk -v num1=\"$i\" -v num2=90% \'BEGIN{print (num1<=num2)?\"0\":\"1\" }\'`if [ $flag -eq 1 ];thenPART=`df -h|awk \'{if(int($5)==\'\'\'$i\'\'\') print $6}\'`echo \"$PART=${i}%\"fidoneecho \"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_RATEdoflag=`awk -v num1=\"$i\" -v num2=90% \'BEGIN{print (num1<=num2)?\"0\":\"1\" }\'`if [ $flag -eq 1 ];thenPART=`df -h|awk \'{if(int($5)==\'\'\'$i\'\'\') print $6}\'`echo \"$PART=${i}%\"elseecho \"UseRate:\"${USE_RATE}echo \"Inode use rate no than 90% of the partition.\"fidoneecho \"------------------\"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=0for i in $CPUdoecho -n \"$i\"let num++if [ $num -eq 6 ];thenecholet num=0fidoneecho \"------------------\"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=0for i in $MEMdoecho -n \"$i\"let num++if [ $num -eq 6 ];thenecholet num=0fidoneecho \"------------------\"break;;traffic)#网络流量while truedoread -p \"Input network cat name (ens[33/37]):\" ens#判断是否存在if [ `ifconfig |grep -c \"\\<$ens\\>\"` -eq 1 ];thenbreakelseecho \"Input error,please input again.\"fidoneecho \"---------------------------\"OLD_RX=`ifconfig ens33 |awk \'{if(NR==5)print $5}\'`OLD_TX=`ifconfig ens33 |awk \'{if(NR==7)print $5}\'`sleep 1NEW_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\"breakecho \"---------------------------\";;esacdonedone