• shell脚本编写


    一、shell script概念

    可以将shell终端解释器作为人与计算机硬件之间的“翻译官”,作为用户与Linux系统内部的通信媒介。

    shell脚本命令的工作方式:

    1.交互式(Interactive):用户每输入一条命令就立刻执行。
    2.批处理(Batch):由用户事先编写好一个完整的shell脚本,脚本会一次性执行完所有的命令。

    在shell script撰写中的注意事项:

    1.命令的执行是从上而下、从左而右进行的。
    2.命令、选项与参数间的多个空格都会被忽略掉。
    3.空白行也将被忽略掉,并且按“Tab”键所生成的空白同样被视为空格键。
    4.如果读取到一个Enter符号(CR),就尝试开始运行该行(或该串)命令。
    5.如果一行的内容太多,则可以使用“[Enter]”来延伸至下一行。
    6.“#”可作为注解。任何加在 # 后面的数据将全部被视为注解文字而被忽略

    配置vscode shell编程环境详细方法:
    https://blog.csdn.net/zz153417230/article/details/103176747

    查看当前系统支持哪些版本的shell

    [root@localhost ~]# cat /etc/shells 
    /bin/sh
    /bin/bash
    /sbin/nologin
    /usr/bin/sh
    /usr/bin/bash
    /usr/sbin/nologin
    

    Linux默认的shell是GNU bash(Bourne Again shell).
    默认的Bash提示符为美元符号$。

    二、基本语法

    1、首行宣告语法

    第一行要使用 #!/bin/bash 宣告这个shell脚本使用的shell名称。
    宣告后,当这个程序被运行时,就能加载 bash 相关环境配置文件。
    如果没有配置,该程序可能因为系统无法判断该程序需要什么shell而会无法运行。

    2、注释语法

    整个script语句中,除了第一行 #! 是用来声明shell之外,其他的 # 都是注释。

    注意:一定要养成注释说明脚本内容、功能、作者、联系方式等的习惯。这样有助于未来程序的改写和调试。

    3、变量输入语法

    可以使用read命令撰写脚本,由执行用户输入对应信息。

    #!/bin/bash
    # Program:
    # User  inputs his first name and last name.
    # Program shows his full name.
    # History:
    # 2021/11/17   hqs     First release
    
    PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
    export PATH
    
    read -p "Please input your first name:"  firstname      # 提示输入用户名
    read -p "Please input your last name:"  lastname   
    echo -e "\nYour full name is : $firstname   $lastname"
    

    4、利用date进行文件创建

    基于date实现将每天的数据都备份成不同的文件名,这样可以让旧的数据也被保存下来而不被覆盖。

    #!/bin/bash
    # 让使用者输入文件名称,并获取用户名变量
    echo -e "I will use 'touch' command to create 3 files. "
    read -p "please input your filename: " fileuser
    
    echo -e "当前输入的用户名:$fileuser"
    
    # 为了避免用户随意按enter,利用变量功能分析文件名是否设置
    filename=${fileuser:-"filename"}
    
    echo -e "当前已接受的文件名:$filename"
    
    # 开始利用date命令来取得所需要的文件名
    date1=$(date --date='2 days ago' +%Y%m%d-%H:%M:%S)   # 前两天的日期,注意+号前有空格
    date2=$(date --date='1 days ago' +%Y%m%d-%H:%M:%S)   # 前一天的日期,注意+号前有空格
    date3=$(date +%Y%m%d-%H:%M:%S)      # 今天的日期
    
    # 配置文件名
    file1=${filename}${date1}
    file2=${filename}${date2}
    file3=${filename}${date3}
    
    # 创建文件
    touch "$file1"
    echo -e "已经创建 $file1"
    touch "$file2"
    echo -e "已经创建 $file2"
    touch "$file3"
    echo -e "已经创建 $file3"
    

    5、数值运算

    可以使用declare来定义变量的类型。
    利用 $(计算式) 来进行数值运算。

    注意:bash shell 系统默认只支持到整数。

    #!/bin/bash
    # Program:
    # User  inputs 2 integer numbers ;program will cross these two numbers.
    # History:
    # 2021/11/24   hqs     First release
    
    PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
    export PATH
    
    echo -e "You should input 2 numbers,i will cross them! \n"
    read -p "please input first number: " firstnu
    read -p "please input second number: " secondnu
    result1=$(($firstnu*$secondnu))
    result2=$(($firstnu/$secondnu))
    result3=$(($firstnu+$secondnu))
    result4=$(($firstnu-$secondnu))
    result5=$(($firstnu%$secondnu))
    
    # 乘
    echo -e "\nThe result of $firstnu *  $secondnu is ==> $result1"
    # 除
    echo -e "\nThe result of $firstnu /  $secondnu is ==> $result2"
    # 加
    echo -e "\nThe result of $firstnu +  $secondnu is ==> $result3"
    # 减
    echo -e "\nThe result of $firstnu -  $secondnu is ==> $result4"
    # 取余
    echo -e "\nThe result of $firstnu %  $secondnu is ==> $result5"
    

    注意:不支持小数,10/100=0。

    在数值运算上,还可以使用 declare -i total=$firstnu*$secondnu,也可以使用$((计算式)),更建议使用后者。

    6、脚本运行对bash环境影响

    (1)直接运行的方式运行脚本
    直接命令或bash或sh来执行脚本时,脚本会使用一个新的bash环境来运行脚本内的命令。

    即:脚本是在子程序的bash内运行的,并当子程序完成后,在子程序内的各项变量或动作将结束而不会传回父程序。

    [root@localhost ~]# sh sh04.sh 
    You should input 2 numbers,i will cross them! 
    please input first number: 100
    please input second number: 10
    The result of 100 *  10 is ==> 1000
    The result of 100 /  10 is ==> 10
    The result of 100 +  10 is ==> 110
    The result of 100 -  10 is ==> 90
    The result of 100 %  10 is ==> 0
    [root@localhost ~]# echo $firstnu
    

    可以看到脚本运行完毕时,子程序bash内的所有数据便被移除。

    (2)利用source运行脚本
    脚本会在父程序运行,各项操作都会在原来的bash中生效。

    [root@localhost ~]# source sh04.sh 
    You should input 2 numbers,i will cross them! 
    please input first number: 100
    please input second number: 10
    The result of 100 *  10 is ==> 1000
    The result of 100 /  10 is ==> 10
    The result of 100 +  10 is ==> 110
    The result of 100 -  10 is ==> 90
    The result of 100 %  10 is ==> 0
    [root@localhost ~]# echo $firstnu
    100
    [root@localhost ~]# echo $result1
    1000
    [root@localhost ~]# echo $result2
    10
    [root@localhost ~]# echo $result3
    110
    [root@localhost ~]# echo $result4
    90
    

    三、判断式——test命令和判断符号[]

    运行结果并不会显示任何信息,但最后我们可以通过 $? 或 && 及|| 来显示整个结果

    1、关于某文件名的“文件类型判断”

    1. -e 该文件名是否存在
    # 用$?来检查结果
    [root@localhost ~]# test -e /root
    [root@localhost ~]# echo $?
    0
    [root@localhost ~]# test -e /dmsai
    [root@localhost ~]# echo $?
    1
    
    # &&和||来显示整个结果
    [root@localhost /]# test -e /media && echo yes || echo no
    yes
    [root@localhost /]# test -e /media111 && echo yes || echo no
    no
    
    1. -f 该文件名是否存在且为文件
    [root@localhost /]# test -f /media && echo yes || echo no
    no
    [root@localhost /]# test -f /root/sh03.sh && echo yes || echo no
    yes
    
    1. -d 该文件名是否存在且为目录
    [root@localhost /]# test -d /root/sh03.sh && echo yes || echo no
    no
    [root@localhost /]# test -d /media && echo yes || echo no
    yes
    
    1. -b 该文件名是否存在且为block device 设备
    
    
    1. -c 该文件名是否存在且为character device设备
    
    
    1. -S 该文件名是否存在且为socket文件
    
    
    1. -p 该文件名是否存在且为FIFO文件
    
    
    1. -L 该文件名是否存在且为一个连接文档
    
    

    2、关于某文件的权限检测

    1. -rwx 检测文件名是否存在和具有的权限
      注意:root权限常有例外
    [hqs@localhost ~]$ chmod 777 example.sh 
    [hqs@localhost ~]$ chmod 100 example1.sh 
    [hqs@localhost ~]$ chmod 200 example2.sh
    [hqs@localhost ~]$ chmod 400 example3.sh 
    
    [hqs@localhost ~]$ ll
    total 16
    ---x------ 1 hqs hqs 71 Nov 30 02:22 example1.sh
    --w------- 1 hqs hqs 71 Nov 30 02:22 example2.sh
    -r-------- 1 hqs hqs 71 Nov 30 02:23 example3.sh
    -rwxrwxrwx 1 hqs hqs 71 Nov 17 23:58 example.sh
    # 检查是否具有可读权限
    [hqs@localhost ~]$ test -r example.sh && echo yes || echo no
    yes
    [hqs@localhost ~]$ test -r example1.sh && echo yes || echo no
    no
    [hqs@localhost ~]$ test -r example2.sh && echo yes || echo no
    no
    [hqs@localhost ~]$ test -r example3.sh && echo yes || echo no
    yes
    
    # 检查是否具有可写权限
    [hqs@localhost ~]$ test -w example.sh && echo yes || echo no
    yes
    [hqs@localhost ~]$ test -w example1.sh && echo yes || echo no
    no
    [hqs@localhost ~]$ test -w example2.sh && echo yes || echo no
    yes
    [hqs@localhost ~]$ test -w example3.sh && echo yes || echo no
    no
    
    # 检查是否具有可执行权限
    [hqs@localhost ~]$ test -x example.sh && echo yes || echo no
    yes
    [hqs@localhost ~]$ test -x example1.sh && echo yes || echo no
    yes
    [hqs@localhost ~]$ test -x example2.sh && echo yes || echo no
    no
    [hqs@localhost ~]$ test -x example3.sh && echo yes || echo no
    no
    
    1. -s检查文件名是否存在且为非空白文件
    
    
    
    1. -ugk检查属性
    # 检查文件名是否存在且具有SUID属性
    
    # 检查文件名是否存在且具有SGID属性
    
    # 检查文件名是否存在且具有Sticky bit属性
    
    

    3、两个整数间的判定

    1. -eq 两个数值相等
    #!/bin/bash
    echo -e "输入两个数值进行判定!"
    read -p "first number n1:" n1
    read -p "second number n2:" n2
    test $n1 -eq $n2 && echo "true,$n1和$n2相等" || echo "false,$n1和$n2不相等"
    
    1. -ne两个数值不相等
    #!/bin/bash
    echo -e "输入两个数值进行判定!"
    read -p "first number n1:" n1
    read -p "second number n2:" n2
    test $n1 -ne $n2 && echo "true,$n1和$n2不相等" || echo "false,$n1和$n2相等"
    
    1. -gt 大于
    #!/bin/bash
    echo -e "输入两个数值进行判定!"
    read -p "first number n1:" n1
    read -p "second number n2:" n2
    test $n1 -gt $n2 && echo "true,$n1大于$n2" || echo "false,$n1不大于$n2"
    
    1. -lt 小于
    #!/bin/bash
    echo -e "输入两个数值进行判定!"
    read -p "first number n1:" n1
    read -p "second number n2:" n2
    test $n1 -lt $n2 && echo "true,$n1小于$n2" || echo "false,$n1不小于$n2"
    
    1. -ge 大于等于
    #!/bin/bash
    echo -e "输入两个数值进行判定!"
    read -p "first number n1:" n1
    read -p "second number n2:" n2
    test $n1 -ge $n2 && echo "true,$n1大于等于$n2" || echo "false,$n1小于$n2"
    
    1. -le 小于等于
    #!/bin/bash
    echo -e "输入两个数值进行判定!"
    read -p "first number n1:" n1
    read -p "second number n2:" n2
    test $n1 -le $n2 && echo "true,$n1小于等于<=$n2" || echo "false,$n1大于$n2"
    

    4、字符串数据判定

    1. -z 判定字符串是否为空
      若字符串为空字符串,则为true。
    [root@localhost ~]# test -z 'asdasdasd' && echo "true,为空字符串" || echo "false,非空字符串"
    false,非空字符串
    [root@localhost ~]# test -z '' && echo "true,为空字符串" || echo "false,非空字符串"
    true,为空字符串
    
    1. -n 判定字符串是否不为空
      若字符串为非空字符串,则为true.若字符串为空字符串,则为false。
    [root@localhost ~]# test -n '' && echo "true,为非空字符串" || echo "false,空字符串"
    false,空字符串
    [root@localhost ~]# test -n 'kobe' && echo "true,为非空字符串" || echo "false,空字符串"
    true,为非空字符串
    
    1. = 判定两个字符串是否相等
      若相等,则回传true.
      注意:等号两边要有空格。
    [root@localhost ~]# test 'kobe'='james' && echo "true,相等字符串" || echo "false,不相等字符串"
    true,相等字符串
    [root@localhost ~]# test 'kobe' = 'james' && echo "true,相等字符串" || echo "false,不相等字符串"
    false,不相等字符串
    [root@localhost ~]# test 'kobe' = 'kobe' && echo "true,相等字符串" || echo "false,不相等字符串"
    true,相等字符串
    
    1. != 判定两个字符串是否不相等
      若相等,则回传false.
    [root@localhost ~]# test 'kobe' != 'kobe' && echo "true,不相等字符串" || echo "false,相等字符串"
    false,相等字符串
    [root@localhost ~]# test 'kobe' != 'curry' && echo "true,不相等字符串" || echo "false,相等字符串"
    true,不相等字符串
    

    5、判断符号[]

    除了使用test之外,其实,我们还可以利用判断符号“[]”(就是中括号)来进行数据的判断。

    书写要点:

    在中括号 [] 内的每个组件都需要有空格键来分隔。
    在中括号内的变量,最好都以双引号括起来。
    在中括号内的常数,最好都以单或双引号括起来。

    [root@localhost ~]# test -z $HOME && echo yes || echo no
    no
    [root@localhost ~]# [ -z "$HOME" ]; echo $?
    1
    [root@localhost ~]# [ "$HOME" == "$MAIL" ]; echo $?
    1
    
    [root@RHEL7-2 scripts]# vim  sh06.sh
    #!/bin/bash
    # Program:
    #     This program shows the user's choice
    # History:
    # 2018/08/25	Bobby	First release
    PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
    export PATH
    read -p "Please input (Y/N): " yn
    [ "$yn" == "Y" -o "$yn" == "y" ] && echo "OK, continue" && exit 0
    [ "$yn" == "N" -o "$yn" == "n" ] && echo "Oh, interrupt!" && exit 0
    echo "I don't know what your choice is" && exit 0
    

    三、条件判断式

    简单的条件判断可以使用 &&和||实现。要实现更多功能需要使用if...then。

    1、if简单条件判断

    # 语法格式:
    if [条件判断式]; then
        条件成立,命令内容
    fi
    
    # 案例:
    #!/bin/bash
    if [ -n "$HOME" ]; then
        echo -e '当前的home目录:' $HOME
    fi
    

    2、if...else多重判断

    # 语法格式:
    if [条件判断式]; then
        条件成立,命令内容
    else
        条件不成立,命令内容
    fi
    
    # 案例:
    #!/bin/bash
    read -p "please input home path:" my_home
    
    if [ "$my_home" = "$HOME" ]; then
        echo -e 'home path input success:' $my_home
    else
        echo -e 'home path input error' $my_home
    fi
    

    3、if...elif...else语法格式

    # 语法格式:
    if [条件判断式一]; then
        条件一成立,命令内容
    elif [条件判断式二]; then
        条件二成立,命令内容
    else
        条件一、二均不成立,命令内容
    fi
    
    # 案例
    #!/bin/bash
    read -p "Please input (Y/N): " yn
    if [ "$yn" == "Y" -o "$yn" == "y" ]; then 
       echo "OK, continue" && exit 0
    elif [ "$yn" == "N" -o "$yn" == "n" ]; then
       echo "NO, interrupt!" && exit 0
    else
       echo "I don't know what your choice is" && exit 0
    fi
    

    4、case...in...esac判断

    case ... esac 为多选择语句,与其他语言中的 switch ... case 语句类似,是一种多分枝选择结构。

    1. 每个 case 分支用右圆括号开始;
    2. 用两个分号 ;; 表示 break,即执行结束,跳出整个 case ... esac 语句
    3. esac(就是 case 反过来)作为结束标记。
    4. 可以用 case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。

    语法格式:

    case  $变量名称in   	   # 关键字为case,变量前有 $ 符
      "第一个变量内容")  	   # 每个变量内容建议用双引号括起来,关键字则为小括号 )
        程序段
        ;;           	      # 每个类别结尾使用两个连续的分号来处理
      "第二个变量内容")
        程序段
        ;;
      *)                  	  # 最后一个变量内容都会用 * 来代表所有其他值
        不包含第一个变量内容与第二个变量内容的其他程序运行段
        exit 1
        ;;
    esac                      # 最终的case结尾!case反过来写
    

    案例:

    #!/bin/bash
    echo '输入 1 到 4 之间的数字:'
    echo '你输入的数字为:'
    read aNum
    case $aNum in
        1)  echo '你选择了 1'
        ;;
        2)  echo '你选择了 2'
        ;;
        3)  echo '你选择了 3'
        ;;
        4)  echo '你选择了 4'
        ;;
        *)  echo '你没有输入 1 到 4 之间的数字'
        ;;
    esac
    

    四、条件循环语句

    1、for条件循环语句

    for 循环语句允许脚本一次性读取多个信息,然后逐一对信息进行操作处理,当要处理的数据有范围时,使用 for 循环语句最为合适。

    for循环语句语法格式:

    for 变量名 in 取值列表
    do
        命令序列
    done
    
    # bash shell支持C式for循环
    for (( 初始值; 限制值; 执行步长 ))
    do
        程序段
    done
    
    # 这种语法适合于数值方式的运算,在for后面括号内的参数的意义如下。
    # 初始值:某个变量在循环当中的起始值,直接以类似i=1设置好。
    # 限制值:当变量的值在这个限制值的范围内,就继续进行循环,例如i<=100。
    # 执行步长:每作一次循环时,变量的变化量,例如i=i+1,步长为1。
    

    (1)普通练习

    # 练习1:编写脚本清空所有arp缓存记录
    #!/bin/bash
    for i in $(arp | tail -n +2|tr -s ' ' |cut -d' ' -f1)
    do
      arp -d $i
    done
    
    # 练习2:产生十个随机数
    for i in {0..9};do echo $RANDOM;done
    
    # 练习3:倒数五秒
    #!/bin/bash
    echo "准备倒数5秒:"
    for i in $(seq 5 -1 1)
    do
      echo -en "$i";sleep 1
    done
    echo -e "开始"
    
    # 练习4:三种动物
    #!/bin/bash
    for animal in dog cat elephant
    do
      echo "There are ${animal}s.... "
    done
    
    
    # 练习5:查看/etc/passwd中信息
    # 先安装finger软件
    yum install finger -y
    # 脚本如下:
    #!/bin/bash
    users=$(cut -d ':' -f1 /etc/passwd)  	    # 获取账号名称
    for username in $users               		# 开始循环
    do
        id $username
        finger $username
    done
    
    # 练习6:侦察网络状态
    #!/bin/bash
    network="192.168.10"              	# 先定义一个网络号(网络ID)
    for sitenu in $(seq 1 100)       	# seq为sequence(连续) 的缩写之意
    do
        # 下面的语句取得ping的回传值是正确的还是失败的
        ping -c 1 -w 1 ${network}.${sitenu} &> /dev/null && result=0  ||  result=1
    		 # 开始显示结果是正确的启动(UP)还是错误的没有连通(DOWN)
        if [ "$result" == 0 ]; then
            echo "Server ${network}.${sitenu} is UP."
        else
            echo "Server ${network}.${sitenu} is DOWN."
        fi
    done
    
    # 练习7:查看某目录下文件权限
    #!/bin/bash
    # 先看看这个目录是否存在啊
    read -p "Please input a directory: " dir
    if [ "$dir" == ""  -o  ! -d  "$dir" ]; then
        echo "The $dir is NOT exist in your system."
        exit 1
    fi
    
    # 开始测试文件
    filelist=$(ls $dir)   			     # 列出所有在该目录下的文件名称
    for filename in $filelist
    do
        perm=""
        test -r "$dir/$filename" && perm="$perm readable"
        test -w "$dir/$filename" && perm="$perm writable"
        test -x "$dir/$filename" && perm="$perm executable"
        echo "The file $dir/$filename's permission is $perm "
    done
    

    (2)高级案例

    案例1:准备用户名称列表users.txt,编写脚本使用read命令读取用户输入得密码,赋值给passwd变量。

    [root@linuxprobe ~]# vim users.txt 
    andy 
    barry 
    carl 
    duke 
    eric 
    george
    
    [root@linuxprobe ~]# vim Example.sh 
    #!/bin/bash 
    read -p "Enter The Users Password : " PASSWD 
    for UNAME in `cat users.txt` 
    do 
        id $UNAME &> /dev/null 
        if [ $? -eq 0 ];then 
            echo "Already exists" 
        else 
            useradd $UNAME &> /dev/null 
            echo "$PASSWD" | passwd --stdin $UNAME &> /dev/null    # 多余信息重定向到/dev/null黑洞中(无回收能力垃圾箱)
            if [ $? -eq 0 ];then 
                echo "$UNAME , Create success" 
            else 
                echo "$UNAME , Create failure" 
            fi 
        fi 
    done
    

    案例2:批量测试主机主机是否在线。
    让脚本从主机列表文件 ipadds.txt中自动读取 IP 地址(用来表示主机)并将其赋值给 HLIST 变量,从而通过判断 ping 命令执行后的返回值来逐个测试主机是否在线。

    [root@linuxprobe ~]# vim ipadds.txt 
    192.168.10.10 
    192.168.10.11 
    192.168.10.12
    
    [root@linuxprobe ~]# vim CheckHosts.sh 
    #!/bin/bash 
    HLIST=$(cat ~/ipadds.txt) 
    for IP in $HLIST 
    do 
        ping -c 3 -i 0.2 -W 3 $IP &> /dev/null 
        if [ $? -eq 0 ] ; then 
            echo "Host $IP is On-line." 
        else 
            echo "Host $IP is Off-line." 
        fi 
    done 
    
    [root@linuxprobe ~]# ./CheckHosts.sh 
    Host 192.168.10.10 is On-line. 
    Host 192.168.10.11 is Off-line. 
    Host 192.168.10.12 is Off-line.
    
    案例3:从1累加到用户输入数值
    
    
    

    2、while条件循环

  • 相关阅读:
    python-序列化与反序列化(loads、load、dumps、dump)
    STM32命名
    批处理参考
    Delphi通过管道执行外部命令行程序(cmd)并获取返回结果
    ubuntu使用备忘
    ubuntu14.04中安装QuartusII9.1步骤
    删除选中数据
    DBGridEh基本操作
    sqlserver 字符串函数
    使用 Delphi Xe 的 TDictionary
  • 原文地址:https://www.cnblogs.com/xiugeng/p/15589502.html
Copyright © 2020-2023  润新知