Bash脚本远程FTP删除N天前文件无效果,求排查及替代方案
问题诊断与修复
核心错误点
你的脚本未删除文件的主要原因是获取远程目录列表的逻辑完全错误,导致后续无有效文件可处理,同时还有几处细节bug:
- 目录列表获取错误:原脚本将ftp会话输出丢到
/dev/null,且ftp内部的ls -l $OUTFILE是试图列出远程服务器上同名文件,而非把目录列表保存到本地$OUTFILE。 - 数组索引计算错误:
expr $FNO+5缺少空格,无法正确计算数值,应使用bash原生算术扩展$((FNO+5))。 - 日期处理bug:当文件时间显示为
月 日 时:分(无年份)时,默认用当前年份,跨年场景下(如1月处理去年12月的文件)会判断错误。 - 命令文件损坏:
cat $CMDFILE | tr -d "\r" > $CMDFILE会先清空文件再写入,导致命令文件为空。
修复后的脚本
#!/bin/bash PROGNAME=$(basename "$0") OUTFILE="/tmp/ftplist.$RANDOM.txt" CMDFILE="/tmp/ftpcmd.$RANDOM.txt" ndays=14 print_usage() { echo "" echo "$PROGNAME - 删除FTP服务器上N天前的文件" echo "" echo "用法: $PROGNAME -s -u -p -f (-d)" echo "" echo " -s FTP服务器地址" echo " -u 用户名" echo " -p 密码" echo " -f 目标目录" echo " -d 天数(默认: $ndays)" echo " -h 显示帮助" echo "" echo "示例: $PROGNAME -s ftp.server.com -u myuser -p mypass -f /data -d 3" echo "" exit } # 解析参数 options=':hs:u:p:f:d:' while getopts "$options" flag do case "$flag" in s) FTPSITE="$OPTARG" ;; u) FTPUSER="$OPTARG" ;; p) FTPPASS="$OPTARG" ;; f) FTPDIR="$OPTARG" ;; d) ndays="$OPTARG" ;; h) print_usage ;; \?) echo "无效选项: -$OPTARG" >&2 exit 1 ;; :) echo "选项 -$OPTARG 需要参数。" >&2 exit 1 ;; esac done shift $((OPTIND - 1)) if [[ -z "$FTPSITE" || -z "$FTPUSER" || -z "$FTPPASS" || -z "$FTPDIR" ]]; then echo "错误: 缺少必要参数" print_usage fi # 计算截止日期(格式YYYYMMDD) TDATE=$(date --date="$ndays days ago" +%Y%m%d) CURRENT_YEAR=$(date +%Y) echo "FTP服务器: $FTPSITE" echo "用户名: $FTPUSER" echo "目标目录: $FTPDIR" echo "将删除早于 $TDATE 的文件" # 正确获取远程目录列表到本地文件 ftp -i -n "$FTPSITE" > "$OUTFILE" 2>&1 <<EOMYF user $FTPUSER $FTPPASS binary cd $FTPDIR ls -l quit EOMYF # 检查是否成功获取列表 if [ ! -s "$OUTFILE" ]; then echo "错误: 无法获取远程目录列表" rm -f "$OUTFILE" exit 1 fi # 创建FTP命令文件 echo "user $FTPUSER $FTPPASS" > "$CMDFILE" echo "binary" >> "$CMDFILE" echo "cd $FTPDIR" >> "$CMDFILE" COUNT=0 # 逐行处理目录列表(支持文件名含空格) while read -r line; do # 跳过非文件行(如total开头的行) if [[ "$line" != -* ]]; then continue fi # 拆分字段,兼容文件名含空格的情况 read -r perm links owner group size month day time_or_year filename <<< "$line" # 处理日期,修复跨年bug if [[ "$time_or_year" == *:* ]]; then file_date_str="$month $day $CURRENT_YEAR" file_date=$(date -d "$file_date_str" +%Y%m%d) # 若计算出的日期晚于当前日期,说明是去年的文件 current_date=$(date +%Y%m%d) if [[ "$file_date" -gt "$current_date" ]]; then file_date=$(date -d "$month $day $((CURRENT_YEAR-1))" +%Y%m%d) fi else file_date=$(date -d "$month $day $time_or_year" +%Y%m%d) fi # 判断是否需要删除 if [[ "$file_date" -lt "$TDATE" ]]; then echo "将删除: $filename" echo "delete \"$filename\"" >> "$CMDFILE" ((COUNT++)) fi done < "$OUTFILE" echo "quit" >> "$CMDFILE" # 执行删除操作 if [[ "$COUNT" -gt 0 ]]; then # 清理Windows格式换行符 sed -i 's/\r$//' "$CMDFILE" ftp -i -n "$FTPSITE" < "$CMDFILE" > /dev/null 2>&1 echo "成功删除 $COUNT 个文件" else echo "没有需要删除的文件" fi # 清理临时文件 rm -f "$OUTFILE" "$CMDFILE"
更优实现方案
原生ftp命令功能有限,处理文件名空格、日期解析等场景容易出错,推荐使用lftp,它支持更简洁的脚本语法,无需手动解析目录列表:
#!/bin/bash PROGNAME=$(basename "$0") ndays=14 print_usage() { echo "" echo "$PROGNAME - 删除FTP服务器上N天前的文件" echo "" echo "用法: $PROGNAME -s -u -p -f (-d)" echo "" echo " -s FTP服务器地址" echo " -u 用户名" echo " -p 密码" echo " -f 目标目录" echo " -d 天数(默认: $ndays)" echo " -h 显示帮助" echo "" exit } # 解析参数 options=':hs:u:p:f:d:' while getopts "$options" flag do case "$flag" in s) FTPSITE="$OPTARG" ;; u) FTPUSER="$OPTARG" ;; p) FTPPASS="$OPTARG" ;; f) FTPDIR="$OPTARG" ;; d) ndays="$OPTARG" ;; h) print_usage ;; \?) echo "无效选项: -$OPTARG" >&2; exit 1 ;; :) echo "选项 -$OPTARG 需要参数。" >&2; exit 1 ;; esac done shift $((OPTIND - 1)) if [[ -z "$FTPSITE" || -z "$FTPUSER" || -z "$FTPPASS" || -z "$FTPDIR" ]]; then echo "错误: 缺少必要参数" print_usage fi # 使用lftp执行删除,自动处理各种边缘场景 lftp -c " open ftp://$FTPUSER:$FTPPASS@$FTPSITE cd $FTPDIR find . -type f -daystart -mtime +$ndays -exec rm {} \; quit " echo "删除操作完成"
注意:需先安装lftp(如
apt install lftp或yum install lftp),它比原生ftp脚本更稳定可靠。
内容的提问来源于stack exchange,提问作者user2503303
相关产品推荐
相关产品推荐

