使用 Unix/Linux 系列操作系统就离不开 shell,shell 本质是和 GUI 一样作为用户和操作系统之间的接口而存在,它实际上是一个能够解释和分析用户键盘输入,执行输入的命令,然后返回结果的解释程序。由于占用资源少,而且具有批处理功能,实际开发和维护当中掌握必要的 shell 技能,势必会大大提升工作效率。
以下记录了一个 shell 脚本实例,是为解决实际生产问题而编写。我使用的是 AIX 下语法较严格的 kshell。
深证通会将基金公司的确认数据文件(开户、认申购定投、赎回分红等等)发送给对接过的小站,最终体现就是不同的小站各推到接收方一个独立的文件夹(文件夹以小站号命名)。我作为接收方有一台存储服务器用于接收小站文件,此时另外一台应用服务器需要从存储服务器获取确认数据文件。数据文件按类别不同分别有 03、04、06 等结尾的,每天会有更新。要求是获取每天的 04、06 新文件,每天的文件分别放在当天日期命名的文件夹下。
- # 远端存储服务器目录结构
- |——download
- |——k0001
- | |——YYYYMMdd_xx_xxx_xxxxxxxx_03.TXT
- | |——YYYYMMdd_xx_xxx_xxxxxxxx_04.TXT
- | |——YYYYMMdd_xx_xxx_xxxxxxxx_06.TXT
- | └──...
- |——k0253
- | |——OFD_xx_xxx_YYYYMMdd_03.TXT
- | |——OFD_xx_xxx_YYYYMMdd_04.TXT
- | |——OFD_xx_xxx_YYYYMMdd_06.TXT
- | └──....
- |──zdfile
- | |——YYYYMMdd_xx_xxx_xxxxxxxx_03.TXT
- | |——YYYYMMdd_xx_xxx_xxxxxxxx_04.TXT
- | |——YYYYMMdd_xx_xxx_xxxxxxxx_06.TXT
- | |——OFD_xx_xxx_YYYYMMdd_03.TXT
- | |——OFD_xx_xxx_YYYYMMdd_04.TXT
- | |——OFD_xx_xxx_YYYYMMdd_06.TXT
- | └──....
- └──...
AIX
- $ oslevel
- 7.1.0.0
- $ echo $SHELL
- /usr/bin/ksh
- $ expect -v
- expect version 5.42.1
首先定义变量给定参数,包括 FTP/SFTP 的用户信息和路径,小站文件夹名称,这里我用数组来存放小站文件夹名称,等下就可以遍历数组获取文件,后续维护也比较方便。可以直接在脚本里写好参数,也可以用传参的方式。
- # 接收参数
- localPath=$1
- remotePath=$2
- serverIP=$3
- sftpUser=$4
- sftpPass=$5
- # 定义变量
- SYSDATE=`date +%Y%m%d`
- STATION_ARR[0]="k0001"
- STATION_ARR[1]="k0253"
- STATION_ARR[2]="zdfile"
先判断本地是否存在当日日期文件夹,不存在则创建文件夹,并赋予权限
,然后转到该目录下。
- 755
- # [函数]处理日期文件夹
- createForlder()
- {
- cd $1
- if [[ ! -d ${SYSDATE} ]]; then
- mkdir ${SYSDATE}
- chmod 755 ${SYSDATE}
- fi
- cd ${SYSDATE}
- }
要想通过 shell 脚本登录 FTP,就需要使用非交互式的方式让脚本自动填充指令信息,FTP 使用 - n 参数打开非交互式操作
- # FTP非交互式操作
- ftp_download()
- {
- ftp -n $1 <<!
- user $2 $3
- prom
- bin
- cd $4
- mget *$sysdate*04.*
- mget *$sysdate*06.*
- bye
- !
- }
如果使用的是 SFTP 协议,那么此协议是没有提供非交互式参数可以使用的,此时有两种方案可以解决,一种就是让远程服务器端保存本机的 MAC 密钥,从而自动验证免密登录。当然对于很多对安全性要求较高的情况来说是不允许这种方式的。另外一种就是使用自动化交互工具 expect,具体实现如下:
- # SFTP非交互式操作
- sftp_download()
- {
- expect <<- EOF
- set timeout 5
- spawn sftp $1@$2
- expect {
- "(yes/no)?" {send "yes\r"; expect_continue}
- "password:" {send "$3\r"}
- }
- expect "sftp>"
- send "cd $4\r"
- set timeout -1
- expect "sftp>"
- send "mget *$sysdate*04.*\r"
- expect "sftp>"
- send "mget *$sysdate*06.*\r"
- expect "sftp>"
- send "bye\r"
- EOF
- }
循环遍历数组 STATION_ARR[] 获得小站文件夹名称,并拼接好远程路径 remoteDir,然后调用函数 ftp_download 或 sftp_download 获取文件。
- for station in ${STATION_ARR[@]}; do
- remoteDir=${remotePath}${station}
- ftp_download ${serverIP} ${sftpUser} ${sftpPass} ${remoteDir}
- # sftp_download ${sftpUser} ${serverIP} ${sftpPass} ${remoteDir}
- done
至此,需求功能已全部实现。完整脚本代码如下:
- #!/usr/bin/ksh
- ############################################################
- ## 功能:从存储服务器获取确认文件
- ## By xiaosong 2017-12-31
- ############################################################
- #------------------------参数说明----------------------------
- #--接收
- # localPath -本地文件路径
- # remotePath -远程文件路径
- # serverIP -远程服务器IP
- # sftpUser -sftp用户名
- # sftpPass -sftp密码
- #--变量
- # SYSDATE -系统日期
- # STATION_ARR[] -小站文件夹数组,新增小站增加此数组即可
- #-----------------------------------------------------------
- # 接收参数
- localPath=$1
- remotePath=$2
- serverIP=$3
- sftpUser=$4
- sftpPass=$5
- # 定义变量
- SYSDATE=`date +%Y%m%d`
- STATION_ARR[0]="k0001"
- STATION_ARR[1]="k0253"
- STATION_ARR[2]="zdfile"
- # [函数]处理日期文件夹
- createForlder()
- {
- cd $1
- if [[ ! -d ${SYSDATE} ]]; then
- mkdir ${SYSDATE}
- chmod 755 ${SYSDATE}
- fi
- cd ${SYSDATE}
- }
- # [函数]SFTP非交互式操作
- sftp_download()
- {
- expect <<- EOF
- set timeout 5
- spawn sftp $1@$2
- expect {
- "(yes/no)?" {send "yes\r"; expect_continue}
- "password:" {send "$3\r"}
- }
- expect "sftp>"
- send "cd $4\r"
- set timeout -1
- expect "sftp>"
- send "mget *$sysdate*04.*\r"
- expect "sftp>"
- send "mget *$sysdate*06.*\r"
- expect "sftp>"
- send "bye\r"
- EOF
- }
- # 获取中登文件
- createForlder ${localPath}
- for station in ${STATION_ARR[@]}; do
- remoteDir=${remotePath}${station}
- sftp_download ${sftpUser} ${serverIP} ${sftpPass} ${remoteDir}
- done
脚本功能函数执行前,可以校验是否传递了完整的参数。若参数个数不对,则直接退出脚本终止执行。
- if [[ $# != 5 ]]; then
- exit
- fi
为脚本增加日志函数,记录脚本运行情况,作为历史记录归档,也方便回查定位问题。
- SYSTIME=`date '+%Y-%m-%d %H:%M:%S'`
- # [函数]脚本运行日志
- wLog(){
- echo "${SYSTIME} $1" >> ${LOGPATH}/DownloadFile.log
- }
如果不是配置 crontab 定时任务执行脚本,而是通过其他方式调用脚本执行,那么可能还需要为脚本设置返回值。
- #-----------------------------------------------------------
- #--返回值RETURNCODE
- # 0 -成功
- # 1 -参数传递异常
- # 2 -处理文件夹异常
- # 3 -获取文件异常
- #-----------------------------------------------------------
- # [函数]脚本执行返回值
- retrunCode()
- {
- if [ ${result} -eq "1" ]; then
- RETURNCODE=$1
- echo ${RETURNCODE}
- fi
- }
然后在关键步骤位置调用返回值处理函数。
- # 校验参数个数
- if [[ $# != 5 ]]; then
- exit
- fi
- result=$?
- retrunCode "1"
- # 处理文件夹
- createForlder ${localPath}
- result=$?
- retrunCode "2"
- # 循环获取文件
- for station in ${STATION_ARR[@]}; do
- remoteDir=${remotePath}${station}
- sftp_download ${sftpUser} ${serverIP} ${sftpPass} ${remoteDir}
- done
- result=$?
- retrunCode "3"
来源: https://segmentfault.com/a/1190000012657346