#!/bin/bash # ============================================================================== # 脚本名称: ftp_backup.sh # 功能描述: 将指定的一个或多个目录通过数据流打包并直接上传到FTP服务器。 # 无需生成临时文件,高效且节省磁盘空间。 # 调用方式: # 1. 交互式输入密码 (推荐, 更安全): # ./ftp_backup.sh 目录1 [目录2] ... # 2. 通过 -p 参数指定密码 (不推荐, 有安全风险): # ./ftp_backup.sh -p '你的密码' 目录1 [目录2] ... # 作者: AI Assistant # 版本: 2.0 # ============================================================================== # --- 配置区 (请在此处修改您的FTP服务器信息) --- # FTP服务器信息 FTP_HOST="your_ftp_server_ip" # FTP服务器IP地址或域名 FTP_PORT="21" # FTP服务器端口 (默认为21) FTP_USER="your_username" # FTP登录用户名 # 密码将在运行时通过交互式输入或 -p 参数提供 # 上传到FTP服务器的哪个目录 (如果不存在,请确保FTP账户有创建权限) FTP_REMOTE_DIR="/backup" # --- 配置区结束 --- # --- 脚本主逻辑 (通常无需修改以下内容) --- # 开启严格模式 set -euo pipefail # 用于存储密码的变量 FTP_PASS="" # 显示使用方法 usage() { echo "用法:" echo " $0 目录1 [目录2] ... # 交互式输入密码 (推荐)" echo " $0 -p '密码' 目录1 [目录2] ... # 通过参数提供密码 (不安全)" echo "" echo "示例:" echo " $0 /var/www/html /etc/nginx" echo " $0 -p 'MyP@ssw0rd!' /home/user" exit 1 } # 检查并安装依赖包 (兼容 Alpine, Debian/Ubuntu, RHEL/CentOS/Fedora) install_package_if_missing() { local package_name="$1" if ! command -v "${package_name}" &> /dev/null; then echo "依赖 '${package_name}' 未找到,正在尝试自动安装..." # 检测包管理器并安装 if command -v apk &> /dev/null; then echo "检测到 Alpine 系统,使用 apk 安装..." sudo apk add "${package_name}" elif command -v apt-get &> /dev/null; then echo "检测到 Debian/Ubuntu 系统,使用 apt-get 安装..." sudo apt-get update && sudo apt-get install -y "${package_name}" elif command -v dnf &> /dev/null; then echo "检测到使用 dnf 的系统 (如 Fedora/RHEL 8+),使用 dnf 安装..." sudo dnf install -y "${package_name}" elif command -v yum &> /dev/null; then echo "检测到使用 yum 的系统 (如 CentOS 7),使用 yum 安装..." sudo yum install -y "${package_name}" else echo "错误: 无法识别您的包管理器,请手动安装 '${package_name}'。" exit 1 fi # 再次检查 if ! command -v "${package_name}" &> /dev/null; then echo "错误: '${package_name}' 安装失败!" exit 1 fi echo "'${package_name}' 安装成功。" else echo "依赖 '${package_name}' 已满足。" fi } # 流式打包并上传 stream_and_upload() { local dirs_to_backup=("$@") # 生成唯一的压缩包名 local archive_name="backup_$(hostname)_$(date +%Y%m%d_%H%M%S).tar.gz" local ftp_url="ftp://${FTP_HOST}:${FTP_PORT}${FTP_REMOTE_DIR}/${archive_name}" echo "=========================================" echo "准备开始流式备份和上传..." echo "目标FTP: ${FTP_HOST}:${FTP_PORT}" echo "远程目录: ${FTP_REMOTE_DIR}" echo "备份内容: ${dirs_to_backup[*]}" echo "远程文件名: ${archive_name}" echo "=========================================" # 使用 tar 和 curl 管道进行流式处理 # tar -czf - : - 表示输出到标准输出 # curl -T - : -T - 表示从标准输入读取文件内容上传 # -u : 提供用户名和密码 # --ftp-create-dirs : 自动创建远程目录 (如果需要) # --ftp-pasv : 使用被动模式 (更兼容防火墙) # 我们使用 PIPESTATUS 来检查管道中任何一个命令是否失败 tar -czpf - "${dirs_to_backup[@]}" | curl -T - -u "${FTP_USER}:${FTP_PASS}" --ftp-create-dirs --ftp-pasv "${ftp_url}" # 检查管道命令的退出状态 if [[ ${PIPESTATUS[0]} -ne 0 || ${PIPESTATUS[1]} -ne 0 ]]; then echo "错误: 打包或上传过程中失败!" echo "tar 退出码: ${PIPESTATUS[0]}, curl 退出码: ${PIPESTATUS[1]}" exit 1 fi echo "=========================================" echo "备份任务全部完成!" echo "文件已成功上传为: ${archive_name}" echo "=========================================" } # ============================================================================== # 主程序入口 # ============================================================================== # 1. 解析命令行参数 while getopts ":p:" opt; do case ${opt} in p ) FTP_PASS="${OPTARG}" ;; \? ) echo "无效的选项: -$OPTARG" 1>&2 usage ;; : ) echo "选项 -$OPTARG 需要一个参数." 1>&2 usage ;; esac done shift $((OPTIND -1)) # 2. 检查是否提供了目录参数 if [ "$#" -eq 0 ]; then usage fi # 3. 如果没有通过 -p 提供密码,则交互式输入 if [[ -z "${FTP_PASS}" ]]; then echo "请输入FTP密码:" # -s: 静默输入,不回显到屏幕 # -r: 允许输入反斜杠,防止特殊字符被转义 read -sr FTP_PASS echo "" # 换行 fi # 4. 验证所有提供的目录是否存在 echo "正在验证输入的目录..." for dir in "$@"; do if [ ! -d "$dir" ]; then echo "错误: 目录 '$dir' 不存在!" exit 1 fi done echo "所有目录验证通过。" # 5. 检查并安装依赖 (curl 和 tar) install_package_if_missing "curl" install_package_if_missing "tar" # 6. 执行流式打包和上传 stream_and_upload "$@" exit 0