Shell编程语法及应用

ZoctopusD 2020-06-22

简介

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

查看运行过程

[ ~]# bash -x my.sh

查看进程ID

[ ~]# pgrep 进程名称 &> /dev/null

查看语法问题

[ ~]#     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 $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

相关推荐