zoukankan      html  css  js  c++  java
  • Shell高级用法-----函数(function)及expect用法

    函数介绍(function用法)

    1、function用法

    1、函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程。

    2、它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运 行,而是shell程序的一部分,定义函数只对当前的会话窗口有效,如果再打开一个窗口再定义另外一个函数,就对另一个窗口有效,两者互不影响。

    3、函数和shell程序比较相似,区别在于以下两种:

    (1)Shell程序在子Shell中运行。

    (2)而Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改。

    2、定义函数

    函数由两部分组成:函数名和函数体

    help function 
    语法一:  f_name (){   
      ...函数体...  
    } 
    语法二:  function f_name { 
       ...函数体...  
    }  
    语法三:  function f_name () {   
       ...函数体...  
    }

    可以使用declare -F 选项进行查看所有定义的函数,用unset 加上变量名 就可以删除定义的变量

    3、函数的定义和使用:

    1、函数的定义和使用:

    (1)可在交互式环境下定义函数

    (2)可将函数放在脚本文件中作为它的一部分

    (3)可放在只包含函数的单独文件中

    2、调用:函数只有被调用才会执行

    调用:给定函数名

    函数名出现的地方,会被自动替换为函数代码

    3、函数的生命周期:被调用时创建,返回时终止

    4、函数返回值

    函数有两种返回值:

    1、函数的执行结果返回值:

    (1) 使用echo等命令进行输出

    (2) 函数体中调用命令的输出结果

    2、函数的退出状态码:

    (1) 默认取决于函数中执行的最后一条命令的退出状态码

    (2) 自定义退出状态码,其格式为:

    return 从函数中返回,用最后状态命令决定返回值:

      (1)return 0 无错误返回。

      (2)return 1-255 有错误返回

    5、使用函数文件

    1、可以将经常使用的函数存入函数文件,然后将函数文件载入shell

    2、文件名可任意选取,但最好与相关任务有某种联系。例如:functions.main

    3、一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用set命 令查看所有定义的函数,其输出列表包括已经载入shell的所有函数

    4、若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载 入此文件

    6、删除shell函数

    1、现在对函数做一些改动后,需要先删除函数,使其对shell不可用。使用unset命 令完成删除函数

    2、命令格式为: unset function_name 

    示例: unset findit

                再键入set命令,函数将不再显示

    3、环境函数

    (1)使子进程也可使用

    (2)声明:export -f function_name

    (3)查看:export -f 或 declare -xf

    7、函数参数

    函数可以接受参数: 传递参数给函数:调用函数时,在函数名后面以空白分隔给定参数列表即可;

    例如“testfunc arg1 arg2 ...”

    在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $# 等特殊变量

    8、函数变量

    变量作用域:

    环境变量:当前shell和子shell有效

    本地变量:只在当前shell进程有效,为执行脚本会启动专用子shell进程; 因此,本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数

    局部变量:函数的生命周期;函数结束时变量被自动销毁

    注意:如果函数中有局部变量,如果其名称同本地变量,使用局部变量

              在函数中定义局部变量的方法:local NAME=VALUE

    实例1:

    第一种写法:如果命令过多,这行执行不太方便

    #!/bin/bash
    func_os_version () {  # 定义一个function函数名为func_os_version,然后在大括号里边定义命令,取出操作系统的版本号,类似于定义别名一样
    sed -nr 's/.* ([0-9]+)..*/1/p' /etc/redhat-release    
    }
    echo OS version is `func_os_version`  # 直接写上定义函数名称,或者用echo 加上反向单引号进行输出结果

    查看输出结果:

    [root@centos-7 ~]# bash osversion.sh 
    OS version is 7

    第二种写法:将定义的函数存放到文件中,并将要执行的脚本与定义的函数以及定义函数的文件名进行关联

    [root@centos-7 ~]# cat functions   # 将定义的函数放到functions文件中
    func_os_version () {
    sed -nr 's/.* ([0-9]+)..*/1/p' /etc/redhat-release
    }
    [root@centos-7 ~]# cat osversion.sh  # 将要执行脚本的函数名和上面定义函数名的文件进行关联
    #!/bin/bash
    source functions  # source functions是关联上面的文件
    func_os_version  # 关联functions里边定义的函数名
    [root@centos-7 ~]# chmod +x osversion.sh   # 对脚本加上执行权限
    [root@centos-7 ~]# ./osversion.sh   # 查看此时的执行结果即可
    7

    实例2:

    第一步:先定义functions函数文件

    [root@centos-7 data]# cat  functions  # 定义functions函数文件
    func_is_digit(){
        if [ ! "$1" ];then  # 如果输入的信息不是空,就为真,但又不是数字
            echo "Usage:func_is_digit number"  # 请输入数字
            return  10 
        elif [[ $1 =~ ^[[:digit:]]+$ ]];then  # 如果输入是数字
            return 0  # return  0 返回的是正确结果,但是不会推出脚本
        else
            echo "Not a  digit"  # 如果上面都不是,就提醒不是数字
            return 1
        fi
    }

    第二步:调用functions函数文件,并对不同的成绩分段进行判断

    [root@centos-7 data]# cat score.sh 
    #!/bin/bash
    source /data/functions  # 调用指定的函数文件的绝对路径
    read -p "Input your score:" SCORE
    func_is_digit $SCORE  # 直接调用上面的functions文件
    if [ $? -ne 0 ];then   #判断上面的命令执行不等于0(不成功)就退出
        exit
    else 
        if [ $SCORE -lt 60 ];then  # 如果成功了,对成绩的三种判断如下。
            echo "You are loser"
        elif [ $SCORE -lt 80 ];then
            echo "soso"
        else 
            echo "very good"
        fi
    fi

    实例3

    生产中function配合case语法:

    #!/bin/bash
    #Author:  liupengju
    #date:    2020-06-22
    #TEL:     xxxxxxxxxx
    #代码发布与回滚
    set -e
    set -u
    
    #adx代码部署变量定义
    ADX_DIR=/gnome/adx
    adx_new_version="gnome-adx-0.0.1-SNAPSHOT-jar-with-dependencies.jar"
    ADX_NEW_MD5=`md5sum $adx_new_version | awk '{ print $1 }'`
    
    #此行需要修改为cf平台的md5码
    ADX_CHK_MD5="43bcfe7594f083a8653126e0896b93ac" 
    
    #directAd代码部署变量定义
    direct_DIR=/gnome/directAd/
    direct_version="direct-ad-0.0.1-SNAPSHOT-jar-with-dependencies.jar"
    direct_MD5=`md5sum $direct_version | awk '{ print $1 }'`
    #direct_old_version=$(ls -l  |tail -n1 | awk '{print $9}')
    
    #此行需要修改为cf平台的md5码
    direct_CHK_MD5="03c3c2fc62b2edfc92e548351010ee9f"
    
    ##########部署directAd代码#############################
    fun_copy_direct_code(){
    	mv $direct_DIR/$direct_version  $direct_DIR/bak/${direct_version}_$(date +"%F-%T") 
            echo "-----上一个版本已经移动到备份目录"
    	cp /data/$direct_version  $direct_DIR  && echo "-----代码复制成功!!!"
    }
    
    fun_chk_direct_code(){
          if [[ "$direct_MD5" == "$direct_CHK_MD5" ]];then
               echo "-----代码校验成功" && echo "代码部署成功后MD5值为:$direct_MD5"
          else
               echo "-----代码校验失败" && exit
          fi
    }
    
    fun_deploy_direct_restart(){
    	#$direct_DIR/restart.sh
    	systemctl restart httpd
    	systemctl restart nginx
    	echo "后端服务重启成功!!!"
    }
    
    fun_chk_direct_port1(){ 
    #验证端口存活状态
    	PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'`
    	PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'`
         for port in $PORT1 $PORT2;do
            echo "The port is:$port------监听端口正常"
         done
    }
    
    #############回滚direct代码###################################
    fun_rollback_direct_code(){
    	cd $direct_DIR/bak
    	direct_old_version=$(ls -l  |tail -n1 | awk '{print $9}')  # 提取上一个版本的jar包
    	mv $direct_DIR/${direct_version} $direct_DIR/bak/${direct_version}_$(date +"%F-%T")
    	mv $direct_DIR/bak/${direct_old_version} $direct_DIR/${direct_version}
    	echo "------旧版本代码移动成功"
    	direct_old_MD5=$(md5sum $direct_DIR/${direct_version} |  awk '{print $1}')
    	echo "代码回滚后MD5值为:$direct_old_MD5"
    }
    
    fun_rollback_direct_restart(){
    	#$direct_DIR/restart.sh
    	systemctl restart httpd
    	systemctl restart nginx
    	echo "--------后端服务重启成功"
    }
    
    fun_chk_direct_port2(){
    #验证端口存活状态
    	PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'`
    	PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'`
        for port in $PORT1 $PORT2;do
            echo "The port is:$port------端口监听正常"
        done
    }
    
    
    #####################adx代码部署########################################
    fun_copy_adx__code(){
    	mv $ADX_DIR/$adx_new_version  $ADX_DIR/bak/${adx_new_version}_$(date +"%F-%T") 
            echo "-----上一个版本已经移动到备份目录"
    	cp /data/$adx_new_version  $ADX_DIR  && echo "-----代码复制成功!!!"
    }
    
    fun_chk_adx_code(){
    	if [[ "$ADX_NEW_MD5" == "$ADX_CHK_MD5" ]];then
    		echo "-----代码校验成功" && echo "代码部署成功后MD5值为:$ADX_NEW_MD5"
    	else
            	echo "-----代码校验失败" && exit
    	fi 
    }
    
    fun_deploy_adx_restart(){
    #$ADX_DIR/restart.sh
    	systemctl restart httpd
    	systemctl restart nginx
    	echo "后端服务已经启动!!!"
    }
    
    #验证端口存活状态
    fun_chk_adx_port1(){
    	PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'`
    	PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'`
          for port in $PORT1 $PORT2;do
        	  echo "The port is:$port------监听的端口正常启动"
          done
    }
    
    
    ###################################adx代码回滚###########################
    fun_rollback_adx_code(){
    	cd $ADX_DIR/bak
    	adx_old_version=$(ls -l  |tail -n1 | awk '{print $9}')
    	mv $ADX_DIR/${adx_new_version} $ADX_DIR/bak/${adx_new_version}_$(date +"%F-%T")
    	mv $ADX_DIR/bak/${adx_old_version} $ADX_DIR/${adx_new_version}
    	echo "------旧版本代码移动成功"
    	adx_old_MD5=$(md5sum $ADX_DIR/${adx_new_version} |  awk '{print $1}')
    	echo "代码回滚后MD5值为:$adx_old_MD5"
    }
    
    fun_rollback_adx_restart(){
    	#$ADX_DIR/restart.sh
    	systemctl restart httpd
    	systemctl restart nginx
    	echo "--------后端服务已经启动"
    }
    
    fun_chk_adx_port2(){
    #验证端口存活状态
    	PORT1=`ss -nlt|grep 8080 |awk -F"[[:space:]]+|:" '{ print $7}'`
    	PORT2=`ss -nlt|grep 8182 |awk -F"[[:space:]]+|:" '{ print $7}'`
        for port in $PORT1 $PORT2;do
            echo "The port is:$port-------端口监听正常"
        done
    }
    
    case $1 in
      direct_deploy)
         fun_copy_direct_code
         fun_chk_direct_code
         fun_deploy_direct_restart
         fun_chk_direct_port1
         ;;
      direct_rollback)
         fun_rollback_direct_code
         fun_rollback_direct_restart
         fun_chk_direct_port2
         ;; 
      adx_deploy)
         fun_copy_adx__code
         fun_chk_adx_code
         fun_deploy_adx_restart
         fun_chk_adx_port1
         ;;
      adx_rollback)
        fun_rollback_adx_code
        fun_rollback_adx_restart
        fun_chk_adx_port2
         ;;
    esac

    实例4:

    #!/bin/bash
    #Auth:   liupengju
    #TEL:    xxxxx
    ########部署完成校验#######
    
    ####验证adserver版本号#############
    fun_chk_adx_version(){
    	ansible adx -m shell -a 'md5sum   /gnome/adx/gnome-adx-0.0.1-SNAPSHOT-jar-with-dependencies.jar' |awk '{print $1}'|sort |head -n62 |tee version_adx |cat -n
    	adx_version=$(ansible adx -m shell -a 'md5sum /gnome/adx/gnome-adx-0.0.1-SNAPSHOT-jar-with-dependencies.jar' |awk '{print $1}'|sort |tail -n62 |uniq -c|awk '{print $2}')
    	echo -e "e[1;32m新发布的版本号为:$adx_versione[0m"
    	version1=$(diff metadata  version_adx)
            if [ -z $version1 ];then
        	   echo -e "e[1;32m代码部署成功 e[0m"
    	else
        	   echo -e "e[1;31m请检查错误  e[0m"
    	fi
    }
    
    ####验证directAd版本号############
    fun_chk_direct_version(){	
            ansible adx -m shell -a ' md5sum   /gnome/directAd/direct-ad-0.0.1-SNAPSHOT-jar-with-dependencies.jar'|awk '{print $1}'|sort |head -n62 |tee version_direct |cat -n
            direct_version=$(ansible adx -m shell -a 'md5sum /gnome/directAd/direct-ad-0.0.1-SNAPSHOT-jar-with-dependencies.jar'|awk '{print $1}'|sort |tail -n62 |uniq -c|awk '{print $2}')
            echo -e "e[1;32m新发布的版本号为:$direct_versione[0m"
            version2=$(diff metadata  version_direct)
            if [ -z $version2 ];then
               echo -e "e[1;32m代码部署成功 e[0m"
            else
               echo -e "e[1;31m请检查错误  e[0m"
            fi
    }
    
    ###验证8080端口状态###############
    fun_chk_8080_port(){
     	chk_ip_8080=$(ansible adx -m shell -a ' netstat -ntulp |grep 8080' |awk '{print $1}' |egrep "[0-9]+.*" |sort | tee data_8080.bak |cat -n)
    	DIR_8080=$(diff metadata  data_8080.bak)
    	if [ -z $DIR_8080 ];then
        	   echo -e "e[1;32m端口检查成功,端口号:8080 e[0m"
    	else
        	   echo -e "e[1;31m请检查错误  e[0m"
    	fi
    }
    
    ####验证8182端口状态#############
    fun_chk_8182_port(){
    	chk_ip_8182=$(ansible adx -m shell -a ' netstat -ntulp |grep 8182' |awk '{print $1}' |egrep "[0-9]+.*" |sort |tee data_8182.bak |cat -n)
    	DIR_8182=$(diff metadata  data_8182.bak)
    	if [ -z $DIR_8182 ];then 
    	    echo -e "e[1;32m端口检查成功,端口号:8182 e[0m"
    	else
    	    echo -e "e[1;31m请检查错误  e[0m"
    	fi
    }
    
    case $1 in
         adx)
           fun_chk_adx_version
           fun_chk_8080_port
           fun_chk_8182_port
           ;;
         direct)
           fun_chk_direct_version
           fun_chk_8080_port
           fun_chk_8182_port
           ;;
    esac
    

    expect命令 

    expect 语法:   

           expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]

    选项

        -c:从命令行执行expect脚本,默认expect是交互地执行的   

          示例:expect -c 'expect " " {send "pressed enter "}

         -d:可以输出输出调试信息   

         示例:expect  -d ssh.exp

    expect中相关命令 :

        spawn:启动新的进程
        send:用于向进程发送字符串
        expect:从进程接收字符串
        interact:允许用户交互,并停留在远程连接的主机上
        exp_continue 匹配多个字符串在执行动作后加此命令 

    expect最常用的语法(tcl语言:模式-动作)

    单一分支模式语法:

    expect “hi” {send “You said hi
    "}

    匹配到hi后,会输出“you said hi”,并换行

    多分支模式语法: 

    expect "hi" { send "You said hi
    " }    
           "hehe" { send "Hehe yourself
    " }    
           "bye" { send "Good bye
    " }

    匹配hi,hello,bye任意字符串时,执行相应输出。等同如下: 

    expect {   
               "hi" { send "You said hi
    "}   
               "hehe" { send "Hehe yourself
    "}   
               "bye" { send  " Good bye
    "} 
    } 

    实例5

    #!/usr/bin/expect 
    set ip 192.168.8.100 
    set user root 
    set password centos 
    set timeout 10 
    spawn ssh $user@$ip 
    expect {
            "yes/no" { send "yes
    ";exp_continue }         
             "password" { send "$password
    " } 
    }  
    interact 

    实例6:shell调用脚本expect

    #!/bin/bash 
    ip=$1  
    user=$2 
    password=$3 
    expect <<EOF   # 开启expect命令多行重定向
    set timeout 20 
    spawn ssh $user@$ip 
    expect {         
            "yes/no" { send "yes
    ";exp_continue }         
            "password" { send "$password
    " } 
    } 
    expect "]#" { send "useradd hehe
    " } 
    expect "]#" { send "echo centos |passwd --stdin hehe
    " } 
    expect "]#" { send "exit
    " } 
    expect eof   # 结束语
    EOF  

    实例7:实现批量复制文件

    1、先编辑IP地址清单

    [root@centos-7 opt]# cat iplist.txt 
    192.168.7.101  
    192.168.7.102  
    192.168.7.103  

    2、书写脚本读取IP地址清单内容

    #!/bin/bash 
    while read ip;do
    user=root
    password=centos
    expect <<EOF 
    set timeout 20 
    spawn ssh $user@$ip 
    expect {         
            "yes/no" { send "yes
    ";exp_continue }         
            "password" { send "$password
    " } 
    } 
    expect "]#" { send "useradd hehe
    " }   # 远程ssh登录后创建用户名
    expect "]#" { send "echo centos |passwd --stdin hehe
    " }  # 设置密码
    expect "]#" { send "exit
    " } 
    expect eof 
    EOF  
    done < iplist.txt

    实例8:根据不同用户名和密码传递公钥,实现免密钥登录

    1、创建IP地址,密码清单

    [root@centos-7 opt]# cat iplist.txt 
    192.168.7.101  wangwang
    192.168.7.102  centos
    192.168.7.103  hahahaha

    2、通过while实现批量读取文件内容

    #!/bin/bash
    ssh-keygen -t rsa -P "" -f /root/.ssh/id_rsa
    while read ip password;do
    user=root
    set timeout 10
    expect << EOF
    spawn ssh-copy-id -i /root/.ssh/id_rsa.pub $user@$ip
    expect {
    "yes/no" { send "yes
    ";exp_continue }
    "password" { send "$password
    " }
    }
    expect eof
    EOF
    done <iplist.txt
    

      

      

  • 相关阅读:
    Jenkins自定义变量共享
    HTML中调用JavaScript的几种情况和规范写法
    [Python] 建 Django 项目
    [Django] Window上通过IIS发布Django网站
    [Python] 怎么把HTML的报告转换为图片,利用无头浏览器
    VMware vSphere Client
    mysql优化
    LINUX 内核的优化
    sed强大的固定替换格式
    nfs以及优化
  • 原文地址:https://www.cnblogs.com/struggle-1216/p/13174266.html
Copyright © 2011-2022 走看看