使用scp和mv跨服务器复制文件时出现文件丢失的问题求助
问题原因分析
你的脚本存在几个关键问题导致文件丢失:
- 多文件时间监控失效:用
stat -c %Z /mnt/deliverFiles/*.txt时,若目录下有多个.txt文件,命令会输出每个文件的修改时间,但赋值给变量时只会保留最后一个文件的时间。如果前面的文件更新而最后一个未变,脚本会错过处理这些文件。 - 通配符展开时序问题:
scp *.txt和mv *.txt的通配符是由shell提前展开的。若在scp执行过程中有新文件传入目录,mv会包含这些新文件,但scp并未传输它们,导致这些文件被移动但未同步到远程服务器。 - 无错误校验:如果scp传输失败(比如网络中断),mv依然会执行,源文件被移走但远程服务器未收到,直接造成文件丢失。
- 可能处理不完整文件:当文件正在被写入时,stat的修改时间会变化,脚本会触发操作,但此时文件可能还未写入完成,scp得到的是不完整的文件,后续mv会把这个不完整文件移走。
解决方案
推荐两种改进方案,优先选择基于inotify的实时监控方案,更高效可靠。
方案1:用inotifywait实时监控(推荐)
利用inotify机制监听目录的文件创建/移动事件,确保只处理完整传入的文件,且逐个处理避免遗漏:
#!/bin/bash SOURCE_DIR="/mnt/deliverFiles" TARGET_REMOTE="server:/home/files/" LOCAL_ARCHIVE="/home/files" TEMP_DIR="/mnt/deliverFiles/.processing" # 创建临时处理目录,避免干扰新传入文件 mkdir -p "$TEMP_DIR" # 监听目录的文件创建、移动完成事件(确保文件写入完毕) inotifywait -m -e create,moved_to --format "%f" "$SOURCE_DIR" | while read FILENAME do # 仅处理txt文件 if [[ "$FILENAME" =~ \.txt$ ]]; then # 原子移动到临时目录,避免后续操作冲突 mv "$SOURCE_DIR/$FILENAME" "$TEMP_DIR/" # 传输到远程服务器,校验执行结果 if scp -p "$TEMP_DIR/$FILENAME" "$TARGET_REMOTE"; then # 传输成功后归档到本地目录 mv "$TEMP_DIR/$FILENAME" "$LOCAL_ARCHIVE/" echo "Successfully processed $FILENAME" else # 传输失败,将文件移回原目录等待重试 mv "$TEMP_DIR/$FILENAME" "$SOURCE_DIR/" echo "Failed to transfer $FILENAME, moved back to source" fi fi done
优势
- 实时响应文件传入,无需轮询等待
- 原子移动文件到临时目录,确保处理的是完整文件
- 单个文件单独处理,传输失败可自动重试
- 避免通配符展开导致的遗漏问题
方案2:改进轮询脚本(无inotify时使用)
如果系统无法安装inotify-tools,可修改轮询逻辑,先将所有待处理文件转移到临时目录再处理:
#!/bin/bash SOURCE_DIR="/mnt/deliverFiles" TARGET_REMOTE="server:/home/files/" LOCAL_ARCHIVE="/home/files" TEMP_DIR="/mnt/deliverFiles/.processing" mkdir -p "$TEMP_DIR" while true do # 先将所有源目录的txt文件移到临时目录(原子操作,避免新文件干扰) for FILE in "$SOURCE_DIR"/*.txt; do # 防止通配符无匹配时的空值 [[ -f "$FILE" ]] || continue mv "$FILE" "$TEMP_DIR/" done # 处理临时目录中的文件 for PROCESS_FILE in "$TEMP_DIR"/*.txt; do [[ -f "$PROCESS_FILE" ]] || continue FILENAME=$(basename "$PROCESS_FILE") if scp -p "$PROCESS_FILE" "$TARGET_REMOTE"; then mv "$PROCESS_FILE" "$LOCAL_ARCHIVE/" echo "Processed $FILENAME" else # 失败则移回原目录,下次轮询重试 mv "$PROCESS_FILE" "$SOURCE_DIR/" echo "Transfer failed for $FILENAME, will retry" fi done sleep 5 done
优势
- 避免原脚本中多文件时间监控的缺陷
- 批量转移文件到临时目录,确保本次处理的文件不会被新传入文件干扰
- 保留错误重试机制,防止传输失败导致的文件丢失
内容的提问来源于stack exchange,提问作者sveer
相关产品推荐
相关产品推荐

