YOLO813

expect脚本实例:跨服务器下载文件

    之前写过一篇文章交互式操控Linux - expect测试简单介绍了下expect,也讲到为什么不用python脚本来写,毕竟这个脚本是在linux服务器上运行,不是每一台云服务器上都有python环境的。刚好,前段时间写的一个脚本文件出了问题,今天来备注下解决方案。

    需求:A服务器需要从B服务器某个目录下拉取文件,并获取所有文件名中的最大数字文件名,然后做一个符号链接。一开始我是写了两个sh脚本和一个expect脚本来完成,但是迁移太麻烦了,所以利用expect的-c参数将其合并到一个sh脚本中,其中需要拉取的文件名类似chain3.pem,fullchain1.pem,privkey21.pem等,但是我不知道目标目录有多少个这样的文件,只知道需要取其数字最大值。

    代码如下,开头的注释是!/bin/bash,而非上次示例的#!/usr/bin/expect:

#!/bin/bash
#author: zhangxiaofei 20210204
#copy ssl pem from ECS3 and create symbolic link

    写了一个expect函数,包裹需要交互操作的内容,expect -c参数指定接下来的内容在命令行(command)中执行,这个命令内的内容需要加引号,以防止被shell打断,也可利用单个-c逐步运行多条以分号(semicolons)分割的命令;在expect脚本中,set用来设置变量,主要部分还是以spawn关键词启动一个拷贝命令,最后执行这个方法expect_func:

# expect reply password and get all files to this server
expect_func(){
        expect -c '
        set passwd "1\$2345@\$110"
        set host "4.8.8.10"
        set username "root"
        set localFolder "/etc/archive"
        set remoteFolder "/etc/archive/abc.com"
        set ports "22"
        set timeout 150
        spawn scp -r -P $ports $username@$host:$remoteFolder $localFolder
        expect {
                "*yes/no*" { send "yes\n";exp_continue }
                        "*password*" { send "$passwd\n" }
                }
        expect eof
        exit
        '
}
expect_func

    接下来就是获取最大文件名中的数字,我的方法是把所有文件名的字母及小数点都去掉,再倒序排列获取第一个值,那么它就是最大的;下文中,ls获取所有文件名,通过管道符 | 交给sed来处理,sed -e表明执行s/[a-zA-Z.]//g脚本,其中s的用法为s/regexp/replacement/,通过正则表达式[a-zA-Z.]将所有字母符合去掉(加入g表明去除所有匹配项而非一项),那么就只剩下数字了;再利用sort对其以数字排序、反转、去重,则得到唯一最大值,最后利用head取第一个值,再拼接成想要的目标文件格式:

# get all pem name from folder and choose the lastest version pem
# define the pem absolute path
archiveFolder="/etc/archive/abc.com"
MaxNum=`ls $archiveFolder | sed -e 's/[a-zA-Z.]//g' | sort -nru | head -n 1`
echo "max number is: "$MaxNum
cert="cert$MaxNum.pem"
chain="chain$MaxNum.pem"
fullchain="fullchain$MaxNum.pem"
privkey="privkey$MaxNum.pem"
echo $cert

    最后增加符号链接,重启Apache:

# the symbolic link pem file absolute path
PemPath="/etc/live/abc.com"
ln -f -s $archiveFolder/$cert $PemPath/cert.pem
ln -f -s $archiveFolder/$chain $PemPath/chain.pem
ln -f -s $archiveFolder/$fullchain $PemPath/fullchain.pem
ln -f -s $archiveFolder/$privkey $PemPath/privkey.pem
# restart Apache
/alidata/server/httpd/bin/httpd -k restart


    本来文章到这里就结束了,但是程序出了BUG,原因在于服务器的密码更改之后passwd中带有$符号,而在linux中,双引号包裹的内容并非单纯的字符串,这类$符号会被判断成变量,查了些资料,最后在passwd尝试加入了反斜杠进行转义,结果真的可以。