收起左侧

利用rclone挂载网盘 将远程文件同步到本地 提升**

2
回复
2141
查看
[ 复制链接 ]

1

主题

1

回帖

0

牛值

江湖小虾

2024-12-19 12:42:46 显示全部楼层 阅读模式
悬赏1飞牛币未解决

[i=s] 本帖最后由 👍👍👍_fxY9f 于 2024-12-19 15:49 编辑 [/i]<br /> <br />

折腾了一天,用AI写了个脚本。用rclone挂载远程网盘,将网盘内容同步到本地。

大概流程

1 FnOS里强制重装rclone (好像FnOS自带的rclone功能不全)

2 配置rclone 挂载远程网盘(只要是rclone支持的网盘都可以挂载)

3 FnOS安装 screen ,在同步远程网盘的时候不需要一直开着shell窗口

4 通过脚本将远程网盘里的部分或者全部内容同步到本地文件夹(rclone同步的时候一不小心就会将本地路径下的文件删掉,只保留远程网盘同步的内容。输入了错误的本地路径可能会导致系统崩溃!在脚本里面尽量避免出现这种麻烦,但是操作依然有风险

下面是脚本内容

#!/bin/bash

# 配置日志路径和大小限制
MAX_LOG_SIZE=5242880  # 5MB
LOG_FILE="/root/.config/rclone/rclone.log"
CHECK_LOG="/root/.config/rclone/check.log"

# 轮换日志文件
rotate_log() {
    local log_file="$1"
    if [ -f "$log_file" ] && [ $(stat --format="%s" "$log_file") -ge $MAX_LOG_SIZE ]; then
        mv -f "$log_file" "$log_file.bak"
        echo "日志文件 $log_file 大小超过 5MB,已将其备份为 $log_file.bak"
        > "$log_file"
    fi
}

# 执行 rclone同步函数
run_rclone_sync() {
    local remote_path="$1"
    local local_path="$2"

    for attempt in {1..3}; do
        rclone sync -q "$remote_path" "$local_path" \
        #rclone copy -q "$remote_path" "$local_path" \
            --update \
            --transfers=32 \
            --checkers=32 \
            --multi-thread-streams=16 \
            --buffer-size=128M \
            --fast-list \
            --contimeout=60s \
            --timeout=300s \
            --retries=5 \
            --low-level-retries=10 \
            --progress \
            --stats=30s \
            --create-empty-src-dirs \
            --log-file="$LOG_FILE"

        if [ $? -eq 0 ]; then
            echo "同步已成功完成。"
            break
        else
            echo "第 $attempt 次同步失败,重试中..."
        fi
    done
}

# 检查是否为文件夹

#is_remote_folder() {
#  local full_remote_path="$1"
#  
#  # 使用 rclone lsf 列出路径下的内容
#  output=$(rclone lsf "$full_remote_path" 2>/dev/null)
#
#  # 判断输出内容的行数是否为1
#  if [ "$(echo "$output" | wc -l)" -eq 1 ]; then
#    # 判断输出的内容是否包含在原路径中
#    if [[ "$output" == *"$full_remote_path"* ]]; then
#      # 拼接成新的路径并检查该路径是否存在
#      new_path="$full_remote_path/$output"
#  
#      # 检查该路径是否存在
#      if rclone lsf "$new_path" >/dev/null 2>&1; then
#        echo "yes"  # 该路径是文件夹
#      else
#        echo "no"   # 该路径是文件
#      fi
#    else
#      echo "yes"  # 输出内容与路径无关,认为是文件夹
#    fi
#  else
#    echo "yes"  # 如果输出不是1行内容,默认认为是文件夹
#  fi
#}
is_remote_folder() {
  local full_remote_path="$1"

  # 使用 rclone lsf 列出路径下的内容
  output=$(rclone lsf "$full_remote_path" 2>/dev/null)

  # 判断路径是否为有效路径(rclone lsf 成功返回)
  if [ $? -eq 0 ]; then
    # 如果 lsf 命令成功执行,说明路径是一个文件夹
    # 判断是否为空文件夹(没有内容)
    if [ -z "$output" ]; then
      echo "yes"  # 空文件夹,输出 yes
    else
      echo "yes"  # 非空文件夹,输出 yes
    fi
  else
    # 如果 lsf 命令失败,说明路径不是文件夹
    echo "no"  # 不是文件夹,输出 no
  fi
}
# 执行 rclone check 并仅记录本次同步失败的文件列表
run_rclone_check() {
    local remote_path="$1"
    local local_path="$2"
    local temp_error_list=$(mktemp)

    rotate_log "$CHECK_LOG"

    for attempt in {1..3}; do
        echo "第 $attempt 次校验中..."
        rclone check "$remote_path" "$local_path" --size-only --quiet --log-file="$CHECK_LOG"
        check_status=$?

        if [ $check_status -eq 0 ]; then
            echo "同步校验成功,无文件丢失。"
            break
        else
            echo "第 $attempt 次校验失败,重试中..."
            run_rclone_sync "$remote_path" "$local_path"
        fi
    done

    grep -iE 'ERROR|FAILED|MISSING' "$CHECK_LOG" | awk '{print $NF}' > "$temp_error_list"
    if [ -s "$temp_error_list" ]; then
        echo "本次同步失败的文件列表:" >> "$LOG_FILE"
        cat "$temp_error_list" >> "$LOG_FILE"
    fi

    rm -f "$temp_error_list"
}

# 读取远程路径
#read_remote_path() {
#    while true; do
#        read -e -p "请输入远程路径: " REMOTE_PATH
#        if [ -n "$REMOTE_PATH" ]; then
#            rclone lsf "$SELECTED_REMOTE:$REMOTE_PATH" > /dev/null 2>&1
#            if [ $? -ne 0 ]; then
#                echo "错误: 远程路径 '$REMOTE_PATH' 不存在。"
#            else
#                break
#            fi
#        fi
#    done
#}
# 读取远程路径 增加判断远程路径为空文件夹,避免同步清空本地文件夹
read_remote_path() {
    while true; do
        read -e -p "请输入远程路径: " REMOTE_PATH
        if [ -n "$REMOTE_PATH" ]; then
            # 检查路径是否存在
            rclone lsf "$SELECTED_REMOTE:$REMOTE_PATH" > /dev/null 2>&1
            if [ $? -ne 0 ]; then
                echo "错误: 远程路径 '$REMOTE_PATH' 不存在。"
            else
                # 如果路径存在,检查是否是一个空文件夹
                result=$(rclone lsf "$SELECTED_REMOTE:$REMOTE_PATH")
                if [ -z "$result" ]; then
                    echo "路径 '$REMOTE_PATH' 是一个空文件夹,会清空本地同名文件夹,请重新输入。"
                else
                    break
                fi
            fi
        fi
    done
}
# 读取本地路径
read_local_path() {
    while true; do
        read -e -p "请输入本地路径: " LOCAL_PATH

        # 检查输入的路径是否存在并且是一个文件
        if [ -e "$LOCAL_PATH" ]; then
            if [ -f "$LOCAL_PATH" ]; then
                echo "错误: '$LOCAL_PATH' 是一个文件,请输入一个目录路径。"
            elif [ -d "$LOCAL_PATH" ]; then
                #echo "路径 '$LOCAL_PATH' 是有效的目录。"
                break
            fi
        else
            # 如果路径不存在,尝试创建该目录
            echo "路径 '$LOCAL_PATH' 不存在,正在创建目录..."
            mkdir -p "$LOCAL_PATH"
            break
        fi
    done
}

# 读取 rclone 远程存储
read_remote_storage() {
    REMOTE_NAMES=($(rclone listremotes | sed 's/://'))
    while true; do
        echo "可用的远程存储:" 
        for i in "${!REMOTE_NAMES[@]}"; do
            echo "$((i+1)). ${REMOTE_NAMES[$i]}"
        done
        echo "0. 退出"

        read -p "请选择远程存储 (1-${#REMOTE_NAMES[@]}, 0: 退出): " STORAGE_INDEX
        if [[ "$STORAGE_INDEX" =~ ^[0-9]+$ ]]; then
            if [ "$STORAGE_INDEX" -ge 1 ] && [ "$STORAGE_INDEX" -le ${#REMOTE_NAMES[@]} ]; then
                SELECTED_REMOTE="${REMOTE_NAMES[$((STORAGE_INDEX-1))]}"
                break
            elif [ "$STORAGE_INDEX" -eq 0 ]; then
                echo "退出脚本..."
                exit 0
            else
                echo "无效的选择,请重新选择。"
            fi
        else
            echo "无效的输入,请输入一个数字。"
        fi
    done
}

# 主程序逻辑
rotate_log "$LOG_FILE"
read_remote_storage
read_remote_path
read_local_path
if [[ "$LOCAL_PATH" != "/" ]]; then
    LOCAL_PATH="${LOCAL_PATH%/}"
fi
if [[ "$REMOTE_PATH" != "/" ]]; then
    REMOTE_PATH="${REMOTE_PATH%/}"
fi
FULL_REMOTE_PATH="$SELECTED_REMOTE:$REMOTE_PATH"
echo "输出完整远程路径 $FULL_REMOTE_PATH"
REMOTE_BASENAME=$(basename "$REMOTE_PATH")

# 如果远程路径是根目录 /
if [[ "$REMOTE_PATH" == "/" ]]; then
    if [[ "$LOCAL_PATH" == "/" ]]; then
        # 情况 1: 本地路径也是根目录 /,警告并强制退出
        echo "警告: 远程路径和本地路径都为根目录 '/',操作已被终止以防止系统损坏。"
        exit 1
    else
        # 情况 2: 本地路径不是根目录 /,继续执行
        echo "警告: 继续执行将会同步远程路径 '/' 到本地路径 '$LOCAL_PATH',可能会删除该路径下原有的文件。"
        read -p "是否继续执行?(y/n): " user_choice
        if [[ "$user_choice" != "y" && "$user_choice" != "Y" ]]; then
            echo "操作已取消。"
            exit 0
        fi

        echo "远程路径为根目录 '/', 本地路径为 '$LOCAL_PATH',继续执行。"
        # 执行同步和校验
        run_rclone_sync "$FULL_REMOTE_PATH" "$LOCAL_PATH"
        run_rclone_check "$FULL_REMOTE_PATH" "$LOCAL_PATH"
    fi

# 远程路径不是根目录 /
else
    is_folder=$(is_remote_folder "$FULL_REMOTE_PATH")
     #可以输出对远程路径的判断如果是文件夹输出 yes 否则输出 no
     #echo "$is_folder"
    if [[ "$LOCAL_PATH" == "/" ]]; then
        # 情况 1: 本地路径是根目录 /,判断远程路径是否为文件夹
        if [[ "$is_folder" == "yes" ]]; then
            REMOTE_BASENAME=$(basename "$REMOTE_PATH")
            # 判断本地路径下是否已经存在该远程文件夹
            if [[ ! -d "$LOCAL_PATH/$REMOTE_BASENAME" ]]; then
                # 本地路径下不存在该文件夹,创建文件夹
                echo "本地路径 '$LOCAL_PATH' 下不存在文件夹 '$REMOTE_BASENAME',正在创建该文件夹..."
                mkdir -p "$LOCAL_PATH/$REMOTE_BASENAME"
            else
                echo "本地路径 '$LOCAL_PATH' 下已存在文件夹 '$REMOTE_BASENAME',跳过创建。"
            fi
        fi
    else
        # 情况 2: 本地路径不是根目录 /,判断远程路径是否为文件夹
    original_local_path="$LOCAL_PATH"
        if [[ "$is_folder" == "yes" ]]; then
            REMOTE_BASENAME=$(basename "$REMOTE_PATH")
            LOCAL_BASENAME=$(basename "$LOCAL_PATH")  
            # 判断本地路径是否包含远程文件夹名
            if [[ "$LOCAL_BASENAME" != "$REMOTE_BASENAME" ]]; then
        LOCAL_PATH="$LOCAL_PATH/$REMOTE_BASENAME"

            fi

            # 判断本地路径下是否已经存在该文件夹
            if [[ ! -d "$LOCAL_PATH" ]]; then
                echo "本地路径 '$original_local_path' 下不存在文件夹 '$REMOTE_BASENAME',正在创建该文件夹..."
                mkdir -p "$LOCAL_PATH"
            else
                echo "本地路径 '$original_local_path' 下已存在文件夹 '$REMOTE_BASENAME',跳过创建。"
            fi
        fi
    fi

    # 无论哪种情况,都在最后执行同步和校验
    run_rclone_sync "$FULL_REMOTE_PATH" "$LOCAL_PATH"
    run_rclone_check "$FULL_REMOTE_PATH" "$LOCAL_PATH"
fi

储存脚本为remote-local.sh到当前文件夹(或者指定的文件夹下然后 cd 到这个文件夹下)

赋予权限 sudo chmod +x remote-local.sh

这里最好是单开一个screen

root用户直接 ./remote-local.sh

非root 用户 sudo ./remote-local.sh

(rclone会自己读取配置文件,好像是必须要root权限,或者有其他办法没研究)

脚本有交互,需要你选择需要同步的远程网盘(即使只配置了一个远程网盘)

要求输入远程网盘需要同步的目录或者文件路径

(由于网络原因,可能无法完整远程路径的查询而报错,重新输入远程路径即可)

要求输入本地存储路径

虽然脚本中添加了几处防止输入错误而覆盖本地原有文件的功能

但是 rclone还是会有覆盖本地文件的风险

比如说 远程目录 /movie下只有一部电影名为 dianying.mp4

你想同步到本地 /vol1/1000/Movie下

本地文件夹下还有很多其他的电影

当你输入

远程路径 /movie

本地路径 /vol1/1000/Movie

那么恭喜你,当你同步完之后 本地/vol1/1000/Movie文件夹下只剩一下一部 dianying.mp4

为了避免这种情况出现

远程路径你可以输入 /movie/dianying.mp4

本地路径 输入/vol1/1000/Movie

这样无论 dianying.mp4 是一个文件还是文件夹 脚本都能实现同步 而不影响本地目录/vol1/1000/Movie下其他内容(脚本自动判断远程路径是文件夹还是文件)

如果远程路径/movie下面有空子文件夹或者子文件夹下只有少数文件 而本地/vol1/1000/Movie文件夹下有同名子文件夹且比远程同名子文件夹下文件多,脚本会删除本地子文件夹下的内容

在输入路径的时候 最后带不带"/" 都不影响 ,脚本中加了去除最后"/"的功能

如果你不小心将远程路径和本地路径都输入成了"/" ,不知道rclone会不会擦除系统 (脚本中加了这种可能的判断,会中断执行)

脚本还添加了最多三次两地文件校验,防止长时间传输网络中断造成的同步失败,尽可能的将远程文件完整的同步到本地

我的是千兆移动网络,看看我的同步速度

飞牛传输速度.png

我不是专门写程序的,轻点喷

欢迎一起交流

喜欢的拿去用

将脚本中 rclone sync 替换为 rclone copy 可以避免覆盖造成删除本地其他文件,同名文件也会被覆盖

操作有风险 同步需谨慎

如果造成你的宝贵数据丢失 本人概不负责

附件: 您需要 登录 才可以下载或查看,没有账号?立即注册
收藏
送赞 2
分享

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

1

主题

923

回帖

390

牛值

共建版主

社区上线纪念勋章社区共建团荣誉勋章飞牛百度网盘玩家

2024-12-19 13:55:59 显示全部楼层
谢谢分享!
不需要这么麻烦了: 不需要什么SSH可以实现挂载,rclone的所有网盘。原理: 1,再本地电脑安装 rclone 并运行生成 rclone.conf 配置(官方文档,如果不会可以使用web-ui) 2,再 fnOS 拉取rclone docker 镜像 3,  详情 回复
2025-4-17 11:03

0

主题

4

回帖

0

牛值

江湖小虾

2025-4-17 11:03:33 显示全部楼层

不需要这么麻烦了:

不需要什么SSH可以实现挂载,rclone的所有网盘。原理:
1,再本地电脑安装 rclone 并运行生成 rclone.conf 配置(官方文档,如果不会可以使用web-ui)

2,再 fnOS 拉取rclone docker 镜像

3,上传 rclone.conf 到fnOS

4,运行 fnOS 的rclone docker,指定配置文件,使用rclone serve运行(sftp,webDAV,smb)都行

5,再fnOS添加远程网盘挂载,选择你刚才运行的服务
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则