shell脚本基础4——function函数、expect

文章详细介绍了Shell脚本中的函数定义、参数传递(包括脚本内外的传参)以及局部变量的使用。同时,讲解了expect命令的作用,它是用于自动化交互的工具,特别是在需要输入密码或进行交互式操作的场景下。文中给出了expect的常用命令和多个示例,展示了如何通过expect进行ssh连接并执行命令。

一、function函数

函数作用:

  1. 函数function是由若干条shell命令组成的语句块,定义函数后,可以通过引用函数实现代码重用和模块化编程,而不是重复写多段代码。
  2. 它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运 行,而是shell程序的一部分,定义函数只对当前的会话窗口有效,如果再打开一个窗口再定义另外一个函数,就对另一个窗口有效,两者互不影响。
  3. 函数和shell程序比较相似,区别在于以下两种:
    • Shell程序在子Shell中运行。
    • 而Shell函数在当前Shell中运行,因此在当前Shell中,函数可以对shell中变量进行修改。

1.1 函数的定义使用

基本了解:

  • 函数由两部分组成:函数名和函数体。
  • 引用函数直接写函数名即可。

注意事项:

  1. 可以function 【函数名】() 定义,也可以直接【函数名】() 定义。比如function qingjun(),也可以写成qingjun(),项目中大都使用简写定义函数。
  2. 参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255)

1.简写式。

[root@localhost ~]# cat qingjun.sh 
#!/bin/bash
qingjun() {
  echo 'hehe'
}

qingjun

在这里插入图片描述
2.全写式。

[root@localhost ~]# cat qingjun.sh 
#!/bin/bash
function qingjun() {
  echo 'hehe'
}

qingjun

在这里插入图片描述

1.2 函数参数

  • 在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数…

1.2.1 脚本内传参

1.传入的第一个参数“start”对应函数里的$1,可选项有“start"、“stop”,若不是这两个选项则直接匹配*;传入的第二个参数“httpd”对应函数里的$2。

[root@localhost ~]# cat qingjun.sh 
#!/bin/bash
my_service(){
    case $1 in
        "start")
            echo "start $2"
            ;;
        "stop")
            echo "stop $2"
            ;;
        *)
            echo "Usage: service $2 start|stop"
            ;;
    esac
}

my_service start httpd

在这里插入图片描述

1.2.2 脚本外传参

  • 函数里的传参变量是根据变量在脚本里的位置来对应的,函数里的$1对应下面传参的第1个数或变量,不管你是不是$1(如下就是下面传入的参数$2对应函数里的$1),函数里的$2对应下面传参的第2个数或变量,以此类推。
  • 函数里的变量只能在脚本里进行传参,若脚本里没有定义传参变量或脚本里没有直接传参,则直接匹配*。

1.例一,脚本里定义传参变量$2,$1,这两个变量值分别对应函数里的$1,$2,所以看到脚本里的传参时不要被名称给误导了,只需按照参数顺序依次对应函数里的$1,$2,$3,$4…即可。

[root@localhost ~]# cat qingjun.sh 
#!/bin/bash
my_service(){
    case $1 in
        "start")
            echo "start $2"
            ;;
        "stop")
            echo "stop $2"
            ;;
        *)
            echo "Usage: service $2 start|stop"
            ;;
    esac
}
my_service $2 $1

在这里插入图片描述
2.例二。

[root@localhost ~]# cat qingjun.sh 
#!/bin/bash
my_service(){
    case $1 in
        "start")
            echo "start $2"
            ;;
        "stop")
            echo "stop $2"
            ;;
        *)
            echo "Usage: service $2 start|stop"
            ;;
    esac
}

my_service $1 $2

在这里插入图片描述
3.例三,脚本不传参,则直接匹配*。

[root@localhost ~]# cat qingjun.sh 
#!/bin/bash
my_service(){
    case $1 in
        "start")
            echo "start $2"
            ;;
        "stop")
            echo "stop $2"
            ;;
        *)
            echo "Usage: service $2 start|stop"
            ;;
    esac
}

my_service

在这里插入图片描述

1.3 引用局部变量

  • 局部变量使用local来定义,比如本地变量是a=10,定义成局部变量为local a=10。
  • 局部变量只对当前代码段生效。

1.3.1 区分局部变量

1.定义本地变量a=10对全局生效,函数里定义局部变量a=20只对当前函数代码块生效,所以在最后echo $a时,用的是全局变量a=10。那为什么没有把函数里的echo $a打印出来?是因为没有引用函数,所以不执行函数里的操作。

在这里插入图片描述
2.此时引用函数,则执行函数体,会把函数里的echo $a打印出来,引用的是函数里的局部变量local a=20。
在这里插入图片描述

1.3.2 全局变量在函数外

1.此时定义第2个全局变量a=30,这样最后一行的echo $a输出的是全局变量a=30,因为后者全局变量会把前者覆盖,而不会覆盖函数里的局部变量a=20。
在这里插入图片描述

1.3.3 全局变量在函数体

  • 当函数里即存在局部变量,也存在全局变量时,需要看两者的先后顺序,因为顺序不同覆盖效果也不同。
    • 若全局变量在局部变量后面,则覆盖函数内的局部变量。
    • 若全局变量在局部变量前面,则覆盖函数外的全局变量。

1.如下示例,有两个全局变量a=10和a=30,后者在函数内,此时a=30会覆盖局部变量a=20,函数外的全局变量不受影响,最后调用函数输出的是30。

[root@localhost ~]# cat qingjun.sh 
#!/bin/bash
a=10
my_service(){
    local a=20
    a=30
    echo $a
}
my_service
echo $a

在这里插入图片描述
2.当全局变量在局部变量前面时,会覆盖函数外的全局变量,函数内的局部变量不受影响。

[root@localhost ~]# cat qingjun.sh 
#!/bin/bash
a=10
my_service(){
    a=30
    local a=20
    echo $a
}
my_service
echo $a

在这里插入图片描述

1.3.4 函数体直接输出具体值

1.当函数体不存在局部变量时,此时会直接输出函数体结果,即时存在全局变量也是覆盖函数外的全局变量。

[root@localhost ~]# cat qingjun.sh 
#!/bin/bash
a=10
my_service(){
    a=30
    echo 50
}
my_service
echo $a

在这里插入图片描述
2.此时函数内添加局部变量,需要看添加位置,若是加在全局变量前面,则会被函数内的全局变量覆盖,但最终引用函数输出的值是具体值50,最后一行输出的值是函数外的全局变量值10。

[root@localhost ~]# cat qingjun.sh 
#!/bin/bash
a=10
my_service(){
    local a=20
    a=30
    echo 50
}
my_service
echo $a

在这里插入图片描述
3.当添加的局部变量在函数里的全局变量后面,则不会受影响,最后一行的echo $a是用的函数里的全局变量a=30,将函数外的a=10覆盖了。

[root@localhost ~]# cat qingjun.sh 
#!/bin/bash
a=10
my_service(){
    a=30
    local a=20
    echo 50
}
my_service
echo $a

在这里插入图片描述

二、expect命令

基本了解:

  • 若需要交互式的命令,比如ssh到其他机器,需要输入密码,scp命令这种需要交互式的输入密码则可以用expect。
  • expect是一个自动化交互套件,主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信。

expect自动交互流程:

  • spawn启动指定进程 ——> expect获取指定关键字 ——>send向指定程序发送指定字符 ——> 执行完成退出

2.1 常用命令

子命令释义
spawn交互程序开始后面跟命令或者指定程序
expect获取匹配信息匹配成功则执行expect后面的程序动作
send exp_send用于发送指定的字符串信息
exp_continue在expect中多次匹配就需要用到
send_user用来打印输出相当于she11中的echo
exit退出expect脚本
eofexpect执行结束 退出
set定义变量
puts输出变量
set_timeout设置超时时间

2.2 安装使用

  • 需要提前安装expect,之后才能在脚本中引用子命令。

1.安装。

yum install -y expect

2.查看。
在这里插入图片描述

2.3 例一

1.远程到192.168.130.161主机上,并且执行命令df -Th查看磁盘资源。

[root@localhost ~]# cat qingjun.sh 
#!/usr/bin/expect            ##固定写法,使用expect执行脚本。
spawn ssh root@192.168.130.161 df -Th     ##spawn固定写法,后面跟需要交互的命令,df -Th是交互完成后要执行的命令。
expect "*password"         ##期望匹配“*password”
send "citms@123\n"         ##发送密码,意思是写交互时要写入的密码。
expect eof                    ##期望结束。

在这里插入图片描述

2.4 例二

  • 例一写法有个弊端,就是当期望太多时,写出来的代码很长,所以可以使用以下这种写法。
[root@localhost ~]# cat qingjun.sh 
#!/usr/bin/expect
spawn ssh root@192.168.130.161 df -Th
expect {
    "*yes/no*" {send "yes\n"; exp_continue}    ##第一个期望,期望匹配“*yes/no*”时输入的值为“yes”,exp_continue代表继续。
    "*password:*" {send "redhat\n"}    ##第二个期望,期望匹配“*password:*”时输入的值为“redhat”,\n表示回车,不然输入密码后不会往后执行。
}
expect eof

2.5 例三

字体颜色字体背景显示方式
30: 黑40: 黑0: 终端默认设置
31: 红41: 红1: 高亮显示
32: 绿42: 绿4: 下划线显示
33: 黄43: 黄5: 闪烁显示
34: 蓝44: 蓝7: 反白显示
35: 紫45: 紫8: 隐藏
36: 深绿46: 深绿
37: 白47: 白

书写格式:

  • \033[1;31;40m :1是显示方式,为可选项。31是字体颜色。40m是字体背景颜色。
  • \033[0m # 恢复终端默认颜色,即取消颜色设置
    在这里插入图片描述

案例:

  • openssl签发CA证书脚本。
[root@localhost ~]# cat qingjun.sh 
#!/bin/bash
#######################################
country=CN
province=HB
city=WH
company=runtime
unit=runtime
domain=www.qingjun.com  ##每次生成新证书修改此处即可。
email=1@2.com
########################################
if [ $UID -ne 0 ];then
    echo "请以管理员身份运行此脚本。"
    exit 120
fi

if [ ! -d /etcd/pki/CA/  ];then
    mkdir -p /etc/pki/CA
    echo -e "\033[1;32m证书目录不存在,即将创建证书目录/etc/pki/CA \033[0m"
   
fi


cd /etc/pki/CA
if [ ! -d private ];then
        mkdir -p private
fi
(umask 077;openssl genrsa -out private/cakey.pem 2048) &>/dev/null

/usr/bin/expect <<EOF
spawn openssl req -new -x509 -key private/cakey.pem -out cacert.pem -days 365 
expect {
    "*code" {send "${country}\n"; exp_continue}
    "Province" {send "${province}\n"; exp_continue}
    "*city" {send "${city}\n"; exp_continue}
    "*Company" {send "${company}\n"; exp_continue}
    "*section" {send "${unit}\n"; exp_continue}
    "*hostname" {send "${domain}\n"; exp_continue}
    "*Address" {send "${email}\n"}
}
expect eof
EOF


mkdir certs newcerts crl &>/dev/null
touch index.txt && echo 01 > serial
cd
(umask 077;openssl genrsa -out ${domain}.key 2048) &>/dev/null


/usr/bin/expect <<EOF
spawn openssl req -new -key ${domain}.key -days 365 -out ${domain}.csr
expect {
    "*code" {send "${country}\n";exp_continue}
    "*Province" {send "${province}\n";exp_continue}
    "*city" {send "${city}\n";exp_continue}
    "*Company" {send "${company}\n";exp_continue}
    "*section" {send "${unit}\n";exp_continue}
    "*hostname" {send "${domain}\n";exp_continue}
    "*Address" {send "${email}\n";exp_continue}
    "*password" {send "\n";exp_continue}
    "An*" {send "\n"}
}
expect eof
EOF

/usr/bin/expect <<EOF
spawn openssl ca -in ${domain}.csr -out ${domain}.crt -days 365
expect {
    "Sign*" {send "y\n";exp_continue}
    "*commit" {send "y\n"}
}
expect eof
EOF
rm -f /root/${domain}.csr
rm -rf /etc/pki/CA

echo -e "\033[1;32m证书已生成,存放目录为:/root/ \033[0m"

在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马瑞晨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值