简介:lftp-4.0.4 是经典命令行文件传输工具的完整源码包,面向 Linux 运维人员、嵌入式开发者及对 FTP/HTTP 协议实现感兴趣的进阶用户。该版本支持 FTP、HTTP、SFTP 等协议,具备断点续传、镜像同步、文件缓存与批处理脚本能力,可解决服务繁忙或网络波动时的传输中断问题。压缩包共 665 个文件,约 2.08MB,核心为 C/C++ 源码(185 个 .h、106 个 .c、100 个 .cc),另含 autotools 构建脚本(m4、am、in)、gettext 多语言资源(po、gmo)及 man 手册等,便于自行编译安装、二次开发或学习其模块化设计。源码树结构完整,configure、Makefile.am、lftpget 等文件一应俱全,适合在 Linux 环境中深入研读。目前已有 106 人学习下载,对于希望理解 lftp 内部实现或定制传输功能的读者,这套源码包是良好起点,尤其适合研究缓存机制、错误恢复与多协议扩展等高级特性。
1. 为什么 2025 年我还在手动编译 lftp-4.0.4.tar.gz
去年有次给客户做老文件服务器迁移,FTP 上堆着十几年的数据,单文件动辄几十 GB。系统自带的 ftp 命令行不支持断点续传,传到一半网线被保洁碰松,全部从头再来。折腾三天后我放弃治疗,翻出 lftp-4.0.4.tar.gz 这个源码包,在本地服务器上手动 configure、make、make install,用 mirror 命令加几个参数,把迁移时间从“不可估量”压缩到“睡一觉就好”。lftp 是个老牌命令行多协议下载工具,4.0.4 这个版本不算新,但胜在行为可预期、参数稳定,尤其适合数据迁移、目录镜像、定时同步这类需要“跑一夜不出幺蛾子”的场景。适合谁用?搞运维、做数据迁移、维护发布服务器的人,以及所有被 wget 和 ftp 命令气到摔键盘的人。
2. 源码编译 lftp 4.0.4:从依赖检查到 make install 的完整链路
2.1 为什么不用包管理器自带的 lftp,非要源码编译
很多人第一反应是“yum install lftp”或“apt install lftp”一步到位。但真实环境里有一个很现实的问题:预编译包不知道链接了哪个版本的 OpenSSL,也不知道编译时开了哪些特性。我遇到过某发行版自带的 lftp 不支持 SFTP 协议,也有遇到过 mirror 的 --parallel 参数行为跟预期不一致。还有另一种需求是内网环境的系统版本太老,官方源已经不维护了,只能自己找源码包编。
源码编译最大的优势是把依赖关系握在自己手里。你想用 OpenSSL 1.1.1 还是系统自带的 LibreSSL,通过 configure 参数控制;你想装到 /usr/local 避免污染系统目录,也通过 --prefix 控制。lftp 4.0.4 这个版本在网上还容易找到,编译依赖不算复杂,主程序加依赖库整个编译过程大概十分钟出头,成本完全可以接受。
2.2 编译前必须确认的依赖:readline、openssl、zlib 一个都不能少
lftp 4.0.4 编译时主要依赖三个库:readline(交互式命令行历史与补全)、openssl(TLS/SSL 支持)、zlib(压缩传输支持)。缺哪个,configure 阶段或者 make 阶段就会报错,提前检查能省掉很多排查时间。常见的做法是先用 pkg-config 检查是否存在:
# 逐个检查依赖库是否已安装且能被 pkg-config 找到 pkg-config --exists openssl && echo "openssl ok" || echo "openssl missing" pkg-config --exists readline && echo "readline ok" || echo "readline missing" pkg-config --exists zlib && echo "zlib ok" || echo "zlib missing"逻辑说明:pkg-config 是 Linux 下最常见的库依赖检查工具,它读取 .pc 文件,文件里记录着头文件路径和链接参数。三条命令分别检查三个库,每个都返回 ok 才继续下一步。
参数说明:如果某一行输出 missing,说明缺的是开发包而不是运行库。常见做法是用系统包管理器安装对应的 -devel 或 -dev 包。不要只装运行库,编译期需要的是头文件,比如 openssl/ssl.h 和 libssl.so 的软链接,这些都在开发包里。有些系统还需要 ncurses 的开发包,因为 readline 依赖 ncurses,漏装的话 make 阶段会报cannot find -ltinfo之类的链接错误。
检查完依赖后,把源码包解压并进入目录:
tar -zxvf lftp-4.0.4.tar.gz cd lftp-4.0.4这两步没有太多玄学,注意解压前用md5sum lftp-4.0.4.tar.gz或sha256sum校验一下文件完整性,避免下载过程中文件损坏导致编译到一半突然报错。我习惯把源码包放在 /usr/local/src 下,方便以后版本升级时查找。
2.3 configure 参数怎么选:prefix、openssl、readline 的取舍
lftp 的 configure 脚本是标准的 autoconf 风格,支持的参数很多。我一般不会全用默认值,因为默认安装到 /usr/local 这个大家都懂,但有些细节值得手动指定。下面是我常用的编译参数组合:
./configure \ --prefix=/usr/local/lftp-4.0.4 \ --with-openssl \ --with-readline \ --with-zlib \ --disable-dependency-tracking逻辑说明:--prefix指定安装目录,这样 lftp 的可执行文件会被装到/usr/local/lftp-4.0.4/bin/下,对应的配置文件在/usr/local/lftp-4.0.4/etc/。用这种“独立目录”安装方式的好处是卸载时直接删目录,完全不污染系统其他部分。后面三个--with-参数告诉 configure 强制使用对应库;--disable-dependency-tracking是加速编译的选项,在一次性编译场景下没有副作用。
参数说明:如果系统里同时装了多个 OpenSSL 版本,可以加--with-openssl=/usr/local/openssl指定具体路径。某些老系统上 OpenSSL 头文件不在默认搜索路径里,编译时还会报找不到头文件的错,这时候需要手动指定 CPPFLAGS 和 LDFLAGS,类似这样:
./configure \ --prefix=/usr/local/lftp-4.0.4 \ CPPFLAGS="-I/usr/local/openssl/include" \ LDFLAGS="-L/usr/local/openssl/lib -Wl,-rpath,/usr/local/openssl/lib"-Wl,-rpath的作用是把库路径写进可执行文件里,不然编译成功后运行时可能因为找不到 libssl.so 而报错。这个参数不加的话,后面每次运行 lftp 都得先export LD_LIBRARY_PATH,很烦。
configure 执行完后,接着编译和安装:
make -j4 make install-j4是并行编译,4 表示 4 个任务同时跑。如果是 8 核机器可以写 8,但不要盲目写大,编译过程中内存不够会直接 OOM。编译完成后 lftp 就装到了指定目录,接下来做软链接:
ln -s /usr/local/lftp-4.0.4/bin/lftp /usr/local/bin/lftp这样做的原因是很多脚本里写的是 lftp 而不是绝对路径,做一个软链接比改脚本更方便。
2.4 安装后立刻验证:版本、协议、基础连接
编译安装完成后绝对不能直接开跑,先确认版本和编译特性是否符合预期:
/usr/local/lftp-4.0.4/bin/lftp --version输出里能看到版本号、协议支持列表、编译时链接的库版本。重点看三行:一是 “Protocols: ftp, ftps, http, https, sftp” 这类协议列表;二是 “TLS/SSL” 相关行是否显示 enabled;三是 Configure 时指定的 prefix 路径,确认是预期的目录而不是系统默认路径。
然后做一个最基础的真机连接测试:
lftp -u username,password -e "ls; bye" 192.0.2.1-u后面逗号前是用户名、逗号后是密码,-e后面是执行完 ls 就退出的命令序列。这里用 192.0.2.1 这种 reserved IP 做示例。如果连接失败,先看是不是防火墙端口问题,再看用户名密码里有没有特殊字符需要转义。这一步通过后,才算真正装好了 lftp 4.0.4。
3. 镜像同步与断点续传:mirror 命令是你最需要的核心武器
3.1 mirror 参数对照:continue、parallel、delete、only-newer
lftp 的 mirror 命令是它最值钱的功能,支持本地与远端之间的双向目录镜像。我用的最多的是从远端拉到本地,也就是mirror remote_path local_path,加上-R参数则反过来把本地推到远端。新手最容易搞混的是-R的方向语义,很多人以为-R是 recursive,实际上 mirror 默认就是递归的,-R代表 reverse,是反向同步。
下面是我惯用的核心参数表:
| 参数 | 作用 | 场景示例 |
|---|---|---|
-c, --continue | 断点续传 | 大文件传输中断后继续 |
-P N, --parallel=N | 并行传输 N 个文件 | 大量小文件加速 |
--delete | 删除远端多余文件 | 严格镜像 |
--only-newer | 只同步更新的文件 | 增量拉取 |
--exclude | 排除指定文件/目录 | 跳过日志和缓存目录 |
--log=FILE | 日志输出到文件 | 排查同步问题 |
--no-empty-dirs | 不同步空目录 | 避免创建无意义目录 |
这张表里的参数我已经用了很多年,实际使用中-c和--parallel组合是最高频的。--delete参数要慎用,因为它的语义是“远端有但本地没有的文件全部删掉”,一旦本地目录不完整,远端数据会丢得一塌糊涂。我亲眼见过同事因为手滑在错误目录下跑 mirror --delete,丢了三天备份数据。
3.2 一套完整的文件同步命令与输出解读
实际执行时,我会把同步命令写成一行,这样可以直接放进定时任务里。下面这个例子是把远端某目录同步到本地备份目录:
lftp -u username,password \ -e "mirror --continue --parallel=4 --exclude '*.tmp' --log=/var/log/lftp-sync.log /data/remote /data/local; bye" \ ftp://192.0.2.1逻辑说明:-e后面双引号是整个 lftp 交互式命令序列,第一步执行 mirror,参数依次是断点续传、4 个文件并行、排除 .tmp 结尾的临时文件、把日志写到固定路径;命令结束后bye退出,避免 lftp 进入交互式界面卡住。末尾的ftp://192.0.2.1是远端地址。
参数说明:--exclude的匹配模式是通配符形式,支持*和?。我想提醒一点:--exclude只对文件名生效,对--parallel并行任务的调度顺序没有影响。--log参数把进度和错误输出写到文件里,建议永远加上,不然几个小时后回来只能看到终端界面上滚过去的几千行输出,什么也查不了。
同步过程中 lftp 的输出大致长这样:
---- Mirroring from ftp://192.0.2.1/data/remote/ to local /data/local -- transferring file 100MB.bin [ETA 00:03:09] (23456789/104857600 bytes)第一行表示镜像源和目标;第二行中的[ETA ...]是预计剩余时间,(23456789/104857600 bytes)是当前已传字节数。如果这一行停在同一位置很久不动,说明网络或服务端有问题。传输完成后,lftp 会输出Mirror completed表示任务结束。
3.3 断点续传的真正原理:.lftp 临时文件与 REST 指令
很多人以为加上-c参数就有续传能力,其实-c只是“如果本地存在同名文件,则尝试从断点继续”。真正支撑续传的是 lftp 在本地生成的一个隐藏临时文件,命名通常类似.lftp-xxxxx,里面记录着已下载的字节偏移。当服务端支持 FTP 的 REST 指令或 HTTP 的 Range 头时,lftp 会发送“从偏移 X 继续”的请求,服务端从那个位置开始发送剩余部分。
这个机制有几个实际影响。第一,不要在 mirror 过程中手动删除隐藏的.lftp-*文件,删了等于自杀。第二,如果服务端不支持 REST,-c不生效,lftp 会默认从头开始传,但不会报明错误,你得通过日志里的resuming at ...字样来判断。第三,续传后的文件不一定和源文件字节完全一致,尤其是最后一小段如果服务端响应异常可能导致文件尾部缺失。所以传输完成后用md5sum校验非常有必要。
3.4 参数调优经验:小文件和大文件要区别对待
同步大量小文件(几百 KB 到几 MB)时,--parallel是最大功臣。默认单线程串行下载,哪怕网络再快也被延迟卡死。但并行数不是越大越好。我做过实测,--parallel=2时带宽还没打满,--parallel=8时某些老服务器开始频繁断开连接,原因是对端限制了并发连接数。一般建议从 4 开始试,如果日志里出现Too many connections或Connection refused,就降为 2。
大文件场景则要单独处理。-c对大文件有用是因为断点续传减少了重复流量,但大文件的瓶颈往往是服务端单线程吞吐。此时--parallel帮不上忙,反而会抢带宽。常见做法是先单独拉大文件:
lftp -e "pget -n 4 -c /data/remote/archive.tar.gz -o /data/local/; bye" ftp://192.0.2.1pget是把单个文件分块并行下载,-n 4表示开 4 个连接同时拉同一文件,-c同样启用续传。这个命令和 mirror 结合使用,大文件用 pget 抢速度,小文件用 mirror 保证完整性,是数据迁移时的黄金组合。
4. 避坑指南:lftp 4.0.4 使用中的高频翻车现场与排查路径
4.1 configure 时报错找不到 OpenSSL 头文件
现象:执行./configure后输出checking for openssl/ssl.h... no,然后中断退出。或者更隐蔽的,configure 通过了,但make阶段报fatal error: openssl/ssl.h: No such file or directory。
原因:系统里只装了 OpenSSL 的运行库(libssl.so),没装开发包(ssl.h 头文件和静态库)。虽然openssl version能正常输出,但编译阶段找不到头文件。
解决:安装对应开发包。不同系统的包名不一样,常见的是libssl-dev或openssl-devel。装完后用pkg-config --exists openssl再验证一次。如果系统比较古老,可以考虑指定--with-openssl=/usr/local/openssl并手动设置 CPPFLAGS 和 LDFLAGS,具体写法在前面 2.3 节已经给出。
4.2 中文文件名在同步后变成乱码
现象:mirror 拉取下来的文件,中文文件名显示成乱码,ls看到的是?????.pdf,无法直接识别原始文件名。
原因:FTP 服务端返回的目录列表使用了传统编码,通常是 GBK,而本地系统是 UTF-8。lftp 默认按 UTF-8 解释字符,把 GBK 的字节流当 UTF-8 解码,自然产生乱码。
解决:在 lftp 交互式环境里设置字符集转换:
set ftp:charset "GBK" set file:charset "UTF-8"分别代表“远端字符集是 GBK,本地字符集是 UTF-8”。如果想全局生效,把这行写进~/lftprc或系统级lftp.conf。注意个别服务器同时含 UTF-8 和 GBK 目录,这时只能按目录切换。乱码问题不要拖到同步之后,mirror 拉下来的文件名如果已经错了,需要删除后重拉。
4.3 加 -c 参数后仍从头传输,日志里看不到 resuming
现象:mirror 使用--continue,文件也刚好中断过,但日志里显示字节数从头开始增长,没有任何续传标志。
原因:最常见的是服务端不支持 FTP 的 REST 命令,或服务端的 FTP 实现有 bug。lftp 得知服务端不支持断点后不会报错,只在日志某行显示REST failed之类信息。另一个常见原因是你手动删除了隐藏的.lftp-*临时文件,lftp 失去了断点记录,只能从头开始。
解决:先用基础 ftp 客户端执行rest 100试试服务端是否支持。不支持的话,搭一个支持断点的服务端最省心,或者退而求其次保传输完整性。另外,放弃手动清理.lftp-*文件的习惯,不要让任何清理脚本去匹配.*。
4.4 mirror 时把日志目录一起拖下来,占了一半带宽
现象:远端目录里有一个 logs 子目录,几个月积累了几百 GB 日志,mirror 每次都全量拖下来,同步耗时极长。
原因:没有在 mirror 命令里加排除规则。很多人以为 mirror 默认会跳过明显不需要的目录,实际上它一视同仁。
解决:在 mirror 参数里加--exclude:
mirror --continue --exclude 'logs/' --exclude 'cache/' --exclude '*.tmp'排除规则可以直接用/结尾匹配目录。还有一个配套参数是--exclude-glob,它和--exclude的区别是匹配基准不同:--exclude匹配路径的一部分,--exclude-glob匹配文件名通配符。用错了会排除不掉预期文件,建议区分使用。
4.5 脚本定时任务里 lftp 报 command not found
现象:手动在终端里执行同步脚本没问题,放进 cron 后日志中出现lftp: command not found。
原因:cron 执行环境的最小化 PATH 变量里不包含你的 lftp 安装路径,例如/usr/local/bin没有暴露给 cron 进程。
解决:在脚本开头显式声明 PATH,或使用绝对路径:
#!/bin/bash export PATH=/usr/local/bin:/usr/bin:/bin L FTP_BIN=/usr/local/lftp-4.0.4/bin/lftp $L FTP_BIN -u username,password -e "mirror ...; bye" ftp://192.0.2.1注意变量名不要写错,我这里拆开写是为了让路径更清晰。更保险的做法是在脚本入口source /etc/profile或者source ~/.bashrc把完整环境导入,但这两个文件里往往有别名定义,有时候也会引入意外变量,我倾向于直接 export 自己需要的几个路径。
5. 把 lftp 4.0.4 变成定时同步工具:脚本模板、退出码与日志习惯
做数据迁移不是跑完一次就结束,定时增量备份才是常态。下面给出一个我常用的同步脚本模板,适合放进 cron 每天凌晨跑一次:
#!/bin/bash # 增量同步远端数据到本地:支持断点续传、并行下载、日志留痕 export PATH=/usr/local/lftp-4.0.4/bin:/usr/local/bin:/usr/bin:/bin SYNC_DATE=$(date +%Y%m%d-%H%M%S) LOG_FILE="/var/log/lftp-sync-${SYNC_DATE}.log" lftp -u username,password \ -e "mirror --continue --parallel=4 --only-newer --exclude 'logs/' --log=${LOG_FILE} /data/remote /data/local; bye" \ ftp://192.0.2.1 if [ $? -eq 0 ]; then echo "lftp sync succeeded at $(date '+%Y-%m-%d %H:%M:%S')" >> /var/log/lftp-sync-main.log else echo "lftp sync FAILED at $(date '+%Y-%m-%d %H:%M:%S')" >> /var/log/lftp-sync-main.log exit 1 fi这个脚本的核心价值在日志文件和退出码判断。lftp 命令执行完成后$?如果为 0 表示成功,非 0 表示有传输错误,脚本通过这个值写一行主日志,方便后续巡检。--only-newer参数确保只拉取远端新增或修改过的文件,配合--continue对已下载的文件做断点补充,整体稳定性很高。
放进 cron 时的注意点:一是前面提到的 PATH,解决方式是脚本开头显式 export;二是加锁防重入,避免上一次任务还没结束下一次又开启:
exec 9>/var/run/lftp-sync.lock if ! flock -n 9; then echo "another lftp sync is running, exit" exit 1 fi把这段加在脚本开头,重复执行的同步任务会被直接挡在外面。我踩过这个坑:某次源站数据暴增,同步超过 24 小时,第二天 cron 又启动了一个新任务,两个 lftp 进程同时写同一目录,最终导致文件互相覆盖,比不跑还糟糕。从那之后我所有的同步脚本都加了 flock 锁。
最后一个习惯是定期检查日志。mirror的--log参数把每个文件的传输结果都记录下来,我习惯每周扫一眼日志里的fail和Retrying关键字,没有异常即可安心。lftp 4.0.4 这个老版本虽然不是最新,但在这种“跑一夜不能翻车”的场景里,它的行为足够稳定,带给我比花哨新功能更大的安全感。希望上面的编译流程、参数选择和避坑经历能帮到你,少走我当年走过的弯路。
本文还有配套的精品资源,点击获取