lftp获取FTP Server端最新文件

最近解一个bug,要频繁编译和烧录FW,每次都要将image从远程的FTP server手动copy到本地,然后使用tftp进行烧录,实在不胜其烦。虽然上一篇简单实用的tftp烧录image脚本提高了烧录效率,但是每次将image拷贝到本地也是麻烦。

这次,我依旧想到了WSL, 使用shell脚本还是更加方便快捷,思路很简单,检查server端的最新文件,得到文件名后使用lftp下载到本地。

获取最新文件

FTP Server文件存储方式是将每天的新数据归档到以当日日期命名的文件夹中,如今天20200430

➜  lftp -u "username,password" ftp://1.2.3.4
lftp username@1.2.3.4:~> ls
04-27-20  02:03PM       <DIR>          20200427
04-29-20  05:36PM       <DIR>          20200429
04-30-20  01:11PM       <DIR>          20200430
lftp username@1.2.3.4:/> cd 20200430
lftp username@1.2.3.4:/20200430> ls
04-30-20  08:45AM             25165953 20200430084545-FW-V1.2.1.4.img
04-30-20  09:09AM             25165953 20200430090947-FW-V1.2.1.4.img
04-30-20  10:04AM             25165953 20200430100405-FW-V1.2.1.4.img
04-30-20  11:02AM             25165953 20200430110247-FW-V1.2.1.4.img
04-30-20  01:11PM             25165953 20200430131110-FW-V1.2.1.4.img
lftp username@1.2.3.4:/20200430> exit
➜  

所以获取当日文件列表时需要先进入目录,然后ls并将结果存到本地tmp文件

dir=$(date '+%Y%m%d')
lftp -u "$ftp_user,$ftp_pass" $ftp_host <<-EOF > $tmpfile
    cd $dir
    ls
EOF

由于ls结果默认以时间顺序排序,最新文件在最后一行,这就好办了,结合tailawk就可以得到最新文件名。

image=$(tail -1 $tmpfile |awk '{print $4}')

知道了文件名,先看下本地有没有下载过,如果有则无需重复下载,如果没有就使用lftpget指令下载到本地。

# return if $image already exist in local
[ -f $image ] && return
[ "x$image" = "x" ] && echo "no image today" && exit 1

lftp -u "$ftp_user,$ftp_pass" $ftp_host <<-EOF
    cd $dir
    get $image
EOF

以上就完成了最新image的获取,结合之前使用tftp烧录image的脚本,更新shell脚本得到下面的完整代码。

完整代码

#!/bin/zsh
# download image from FTP server and burn it into router with tftp

burn()
{
    tftp 192.168.1.1 <<-EOF > /dev/null
        binary
        put $1
    EOF
}

dl_newest_image()
{
    ftp_user="username"
    ftp_pass="password"
    ftp_host="ftp://1.2.3.4"
    tmpfile=~/tmp/ftp_list

    # get ftp file list of today
    dir=$(date '+%Y%m%d')
    lftp -u "$ftp_user,$ftp_pass" $ftp_host <<-EOF > $tmpfile
        cd $dir
        ls
    EOF

    image=$(tail -1 $tmpfile |awk '{print $4}')

    # return if $image already exist in local
    [ -f $image ] && return
    [ "x$image" = "x" ] && echo "no image today" && exit 1

    lftp -u "$ftp_user,$ftp_pass" $ftp_host <<-EOF
        cd $dir
        get $image
    EOF
}


cd $DT/tmp/imgs/ # $DT代表Win10的Desktop, 是我.zshrc的一个环境变量
image=$1

if [ "x$1" = "x"  ]; then
    dl_newest_image
fi

echo "start burning $image"
burn $image

使用方法

为了方便,脚本文件名更新为bi, burn image的意思,使用方法含带参和不带参两种。

  1. 带参 - 烧录指定文件
  2. 不带参 - 从FTP server获取最新文件并烧录
➜  bi 20200430110247-FW-V1.2.1.4.img
start burning 20200430110247-FW-V1.2.1.4.img
➜  bi
`20200430131128-RBR20-V2.7.1.8RC1-ubi4.img` at 15953920 (45%) 7.41M/s eta:2s [Receiving data]
start burning 20200430131128-RBR20-V2.7.1.8RC1-ubi4.img

Notice

  1. 其实使用curl也可以获取FTP Server的列表信息,使用wget也可以下载FTP Server文件,但是鉴于公司网络代理环境复杂,容易莫名失败,所以还是使用lftp
  2. 用户名密码不该直接放脚本内部,可以单独存到配置文件并设置好权限,然后脚本去读文件,当然啦,私人电脑忽略;
  3. 脚本中<<-EOF-表示忽略下面输入行每行开始的tab键,所以被EOF包含的部分不能使用空格键替代tab键,否则会出错。