随机
Enter 搜索 ↑↓ 切换 Esc 清空

lx_lrzsz

脚本

Zmodem 文件传输管理器(sz/rz 交互式工具集)

lx_lrzsz

Zmodem 文件传输管理器(sz/rz 交互式工具集)

一键脚本

bash <(curl -sL gitee.com/meimolihan/cmdbox/raw/master/sh/lx_lrzsz.sh)
bash <(curl -sL ../sh/lx_lrzsz.sh)

效果预览

执行脚本效果预览

补充说明

Zmodem 文件传输管理器,基于 lrzsz 提供文件上传/下载、文件夹压缩传输、批量操作等交互式功能。支持 MobaXterm、Xshell、SecureCRT、Tabby、WindTerm 等主流终端软件。

语法

lx_lrzsz.sh

主菜单

选项 功能 说明
1 上传文件到服务器 直接通过 rz 选择单个文件上传
2 下载文件到本地 通过 sz 下载文件到本地
3 选择文件下载 交互式多选文件下载,支持序号/范围/逗号分隔
4 批量上传多个文件 rz -bye 批量上传模式
5 上传文件夹压缩包 上传 ZIP/TAR 压缩包后自动解压
6 下载文件夹压缩包 选择文件夹压缩为 tar.gz/zip/tar.bz2/tar.xz 后下载
7 创建测试文件 创建 1KB/1MB/10MB/自定义大小测试文件
8 清理临时文件 查找并清理 .tmp/.temp/.swp 等临时文件
9 检查终端 Zmodem 支持 检测 lrzsz 安装状态和终端兼容性
10 查看传输历史记录 显示最近修改文件和大文件列表

功能特点

注意事项

脚本源码

#!/bin/bash
set -uo pipefail

gl_hui='\e[37m'
gl_hong='\033[31m'
gl_lv='\033[32m'
gl_huang='\033[33m'
gl_lan='\033[34m'
gl_zi='\033[35m'
gl_bufan='\033[96m'
gl_bai='\033[97m'

log_info()  { echo -e "${gl_lan}[信息]${gl_bai} $*"; }
log_ok()    { echo -e "${gl_lv}[成功]${gl_bai} $*"; }
log_warn()  { echo -e "${gl_huang}[警告]${gl_bai} $*"; }
log_error() { echo -e "${gl_hong}[错误]${gl_bai} $*" >&2; }

break_end() {
    echo -e "${gl_lv}操作完成${gl_bai}"
    echo -e "${gl_bai}按任意键继续 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    read -r -n 1 -s -p ""
    echo ""
    clear
}

exit_script() {
    echo ""
    echo -ne "${gl_hong}感谢使用,再见! ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.5
    echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.6
    clear
    exit 0
}

sleep_fractional() {
    local seconds=$1
    if sleep "$seconds" 2>/dev/null; then return 0; fi
    if command -v python3 >/dev/null 2>&1; then python3 -c "import time; time.sleep($seconds)"; return 0; fi
    local int_seconds=$(echo "$seconds" | awk '{print int($1+0.999)}')
    sleep "$int_seconds"
}

exit_animation() {
    echo -ne "\r${gl_lv}即将退出 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.5
    echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.6
    echo ""
}

handle_invalid_input() {
    echo -ne "\r\033[K${gl_huang}无效的输入,请重新输入! ${gl_zi} 1 ${gl_huang} 秒后返回"
    sleep_fractional 1
    echo -ne "\r\033[K${gl_lv}无效的输入,请重新输入! ${gl_zi}0${gl_lv} 秒后返回"
    sleep_fractional 0.5
    echo -ne "\r\033[K"
    continue
}

cancel_return() {
    local menu_name="${1:-上一级选单}"
    echo -ne "${gl_lv}即将返回 ${gl_huang}${menu_name} ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.5
    echo -ne "${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\c"
    sleep_fractional 0.6
    echo ""
    clear
}

safe_read() {
    local prompt="$1"
    local var_name="$2"
    local type="$3"
    read -r -p "$(echo -e "${gl_bai}$prompt")" "$var_name"
}

display_horizontal_list() {
    local -n arr=$1
    local count=0 items_per_line=2 term_width=$(tput cols 2>/dev/null || echo 80)
    ((term_width > 120)) && items_per_line=4
    ((term_width > 80 && term_width <=120)) && items_per_line=3
    local max_len=0

    for item in "${arr[@]}"; do
        ((${#item} > max_len)) && max_len=${#item}
    done
    max_len=$((max_len + 4))

    for i in "${!arr[@]}"; do
        count=$((count+1))
        printf "${gl_huang}%2d.${gl_bai} %-${max_len}s" "$((i+1))" "${arr[$i]}"
        ((count % items_per_line == 0)) && echo ""
    done
    ((count % items_per_line != 0)) && echo ""
}

install() {
    [[ $# -eq 0 ]] && { log_error "未提供软件包参数!"; return 1; }

    local pkg mgr ver installed
    for pkg in "$@"; do
        installed=false
        ver=""

        if command -v "$pkg" &>/dev/null; then
            ver=$("$pkg" --version 2>/dev/null | head -n1 | grep -oE '[0-9]+(\.[0-9]+)+' | head -n1 || true)
            installed=true
        fi

        if [[ "$pkg" == "7zip" || "$pkg" == "7z" ]] && command -v 7z &>/dev/null; then
            ver=$(7z 2>&1 | grep -oE '[0-9]+(\.[0-9]+)+' | head -n1 || true)
            installed=true
        fi

        if [[ "$installed" == true ]]; then
            echo -e "${gl_huang}${pkg}${gl_bai} ${gl_lv}已安装${gl_bai} ${gl_lv}${ver:-}${gl_bai}"
            continue
        fi

        log_info "${gl_bai}开始安装:${gl_hong}$pkg ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        local install_success=false

        for mgr in opkg dnf yum apt apk pacman zypper pkg; do
            command -v "$mgr" &>/dev/null || continue

            case $mgr in
                opkg) opkg update && opkg install "${pkg//7zip/p7zip}" && install_success=true ;;
                dnf)  dnf -y install "$pkg" && install_success=true ;;
                yum)  yum -y install "$pkg" && install_success=true ;;
                apt)  apt update -y && apt install -y "$pkg" && install_success=true ;;
                apk)  apk add "$pkg" && install_success=true ;;
                pacman) pacman -S --noconfirm "$pkg" && install_success=true ;;
                zypper) zypper install -y "$pkg" && install_success=true ;;
                pkg) pkg install -y "$pkg" && install_success=true ;;
            esac
            [[ "$install_success" == true ]] && break
        done

        if [[ "$install_success" == true ]]; then
            log_ok "$pkg 安装成功"
        else
            log_error "$pkg 安装失败"
        fi
    done
}

###### 上传文件到服务器
rz_upload_file() {
    install lrzsz
    clear
    if ! list_files "." 0 4; then
        return
    fi
    echo -e ""
    echo -e "${gl_zi}>>> 上传文件到服务器 (rz)${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    log_info "准备接收来自本地的文件 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    echo -e "${gl_bai}终端支持检测:${gl_bai}"
    echo -e "${gl_bai}支持的终端: ${gl_lv}MobaXterm, Xshell, SecureCRT, Tabby, WindTerm${gl_bai}"
    echo -e "${gl_huang}注意: PuTTY 不支持Zmodem协议${gl_bai}"
    echo -e "${gl_huang}注意: Windows Terminal 需安装lrzsz并配置${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    echo -e "${gl_huang}请在本地客户端选择要上传的文件 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"

    for i in {2..1}; do
        echo -ne "${gl_huang}将在 ${gl_lv}$i${gl_bai} 秒后弹出文件选择对话框${gl_bai}\r"
        sleep_fractional 1
    done
    echo ""

    if command -v rz >/dev/null 2>&1; then
        echo -e "${gl_bai}执行命令: ${gl_zi}rz${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_huang}提示: 按${gl_lv}Ctrl+C${gl_bai}取消上传${gl_bai}"

        if rz; then
            log_ok "文件上传成功!"
            echo -e "${gl_bai}上传的文件:${gl_bai}"
            ls -lth | head -5
        else
            if [[ $? -eq 130 ]]; then
                log_info "上传被用户取消"
            else
                log_warn "文件上传失败,请检查终端是否支持Zmodem"
            fi
        fi
    else
        log_error "rz命令不可用,请先安装lrzsz"
    fi
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

###### 上传文件夹
rz_upload_compressed_file() {
    clear
    echo -e "${gl_zi}>>> 上传文件夹到服务器 (压缩上传)${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    log_info "此功能将本地文件夹压缩后上传到服务器"
    echo -e "${gl_bai}支持格式:${gl_bai}"
    echo -e "  ${gl_bufan}1. ${gl_bai}通过rz上传ZIP/TAR压缩包"
    echo -e "  ${gl_bufan}2. ${gl_bai}自动解压到当前目录"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    local -a files_before
    files_before=(*)

    read -r -e -p "$(echo -e "${gl_bai}是否自动解压? (${gl_lv}y${gl_bai}/${gl_hong}n${gl_bai}): ")" extract_choice

    for i in {2..1}; do
        echo -ne "${gl_huang}将在 ${gl_lv}$i${gl_bai} 秒后开始上传 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}\r"
        sleep_fractional 1
    done
    echo ""

    if ! command -v rz >/dev/null 2>&1; then
        log_error "rz命令不可用,请先安装 lrzsz"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        exit_animation
        return 1
    fi

    echo -e "${gl_huang}提示: 按${gl_lv}Ctrl+C${gl_bai}取消上传${gl_bai}"
    echo -e "${gl_lv}请在选择文件对话框中选择要上传的压缩包 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"

    if ! rz; then
        local exit_code=$?
        if [[ $exit_code -eq 130 ]]; then
            log_info "上传被用户取消"
        else
            log_warn "文件上传失败,请检查终端是否支持Zmodem"
        fi
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        exit_animation
        return 1
    fi

    local -a files_after
    local -a new_files
    files_after=(*)
    new_files=()

    for f in "${files_after[@]}"; do
        local is_new=true
        for old in "${files_before[@]}"; do
            [[ "$f" == "$old" ]] && is_new=false && break
        done
        [[ "$is_new" == true ]] && new_files+=("$f")
    done

    if [[ ${#new_files[@]} -eq 0 ]]; then
        log_warn "未检测到新上传的文件"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        exit_animation
        return 1
    fi

    local zip_file
    if [[ ${#new_files[@]} -eq 1 ]]; then
        zip_file="${new_files[0]}"
        log_ok "检测到上传文件: ${gl_huang}${zip_file}${gl_bai} (${gl_lv}$(du -h "$zip_file" 2>/dev/null | cut -f1)${gl_bai})"
    else
        log_info "检测到多个新文件:"
        local i=1
        for f in "${new_files[@]}"; do
            echo -e "  ${gl_lv}${i}.${gl_bai} $f ${gl_huang}($(du -h "$f" 2>/dev/null | cut -f1)${gl_bai})"
            ((i++))
        done
        read -r -e -p "$(echo -e "${gl_bai}请选择要处理的文件编号 [1-${#new_files[@]}]: ")" choice
        if [[ "$choice" =~ ^[0-9]+$ ]] && (( choice >= 1 && choice <= ${#new_files[@]} )); then
            zip_file="${new_files[$((choice-1))]}"
        else
            log_error "无效选择"
            exit_animation
            return 1
        fi
    fi

    if [[ "$extract_choice" == "y" || "$extract_choice" == "Y" ]]; then
        log_info "正在解压: ${gl_huang}${zip_file}${gl_bai}"
        local extract_success=false

        if [[ "$zip_file" == *.zip ]]; then
            if command -v unzip >/dev/null 2>&1; then
                if unzip -o "$zip_file" 2>/dev/null; then
                    log_ok "ZIP解压完成 → 当前目录"
                    extract_success=true
                else
                    log_error "ZIP解压失败"
                fi
            else
                log_error "缺少unzip命令: apt install unzip / yum install unzip"
            fi
        elif [[ "$zip_file" == *.tar.gz || "$zip_file" == *.tgz ]]; then
            if [[ -f "$zip_file" ]] && file "$zip_file" | grep -q "gzip compressed data"; then
                if tar -xzf "$zip_file" 2>/dev/null; then
                    log_ok "TAR.GZ解压完成 → 当前目录"
                    extract_success=true
                else
                    log_error "TAR.GZ解压失败,可能是文件损坏或权限问题"
                fi
            else
                log_error "文件格式不匹配或文件已损坏"
            fi
        elif [[ "$zip_file" == *.tar.bz2 ]]; then
            if [[ -f "$zip_file" ]] && (file "$zip_file" | grep -q "bzip2 compressed data" || file "$zip_file" | grep -q "tar archive"); then
                if tar -xjf "$zip_file" 2>/dev/null; then
                    log_ok "TAR.BZ2解压完成 → 当前目录"
                    extract_success=true
                else
                    log_error "TAR.BZ2解压失败,可能是文件损坏或权限问题"
                fi
            else
                log_error "文件格式不匹配或文件已损坏"
            fi
        elif [[ "$zip_file" == *.tar.xz ]]; then
            if [[ -f "$zip_file" ]] && (file "$zip_file" | grep -q "XZ compressed data" || file "$zip_file" | grep -q "tar archive"); then
                if tar -xJf "$zip_file" 2>/dev/null; then
                    log_ok "TAR.XZ解压完成 → 当前目录"
                    extract_success=true
                else
                    log_error "TAR.XZ解压失败,可能是文件损坏或权限问题"
                fi
            else
                log_error "文件格式不匹配或文件已损坏"
            fi
        elif [[ "$zip_file" == *.tar ]]; then
            if [[ -f "$zip_file" ]] && (file "$zip_file" | grep -q "tar archive" || file "$zip_file" | grep -q "POSIX tar archive"); then
                if tar -xf "$zip_file" 2>/dev/null; then
                    log_ok "TAR解压完成 → 当前目录"
                    extract_success=true
                else
                    log_error "TAR解压失败,可能是文件损坏或权限问题"
                fi
            else
                log_error "文件格式不匹配或文件已损坏"
            fi
        elif [[ "$zip_file" == *.gz && ! "$zip_file" == *.tar.gz ]]; then
            if [[ -f "$zip_file" ]] && (file "$zip_file" | grep -q "gzip compressed data"); then
                if gunzip -f "$zip_file" 2>/dev/null; then
                    log_ok "GZ解压完成 → 当前目录"
                    extract_success=true
                else
                    log_error "GZ解压失败,可能是文件损坏或权限问题"
                fi
            else
                log_error "文件格式不匹配或文件已损坏"
            fi
        else
            log_warn "不支持的格式: ${gl_huang}${zip_file##*.}${gl_bai},请手动解压"
        fi

        if [[ "$extract_success" == true ]]; then
            read -r -e -p "$(echo -e "${gl_bai}是否删除压缩包? (${gl_lv}y${gl_bai}/${gl_hong}n${gl_bai}): ")" delete_choice
            if [[ "$delete_choice" == "y" || "$delete_choice" == "Y" ]]; then
                rm -f "$zip_file" 2>/dev/null && log_ok "已删除压缩包" || log_warn "删除失败"
            fi
        fi
    else
        if [[ -f "$zip_file" ]]; then
            log_info "文件已保存: ${gl_huang}$(pwd)/${zip_file}${gl_bai}"
        else
            log_warn "文件不存在或已被移动"
        fi
    fi

    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

###### 下载文件夹
rz_download_folder() {
    clear
    echo -e "${gl_zi}>>> 下载文件夹到本地 (压缩下载)${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    local dir_index=1
    local dir_array=()
    local dir_names=()

    echo -e "${gl_bai}当前目录文件夹列表:${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    while IFS= read -r -d '' dir; do
        if [[ -d "$dir" ]] && [[ "$dir" != "." ]] && [[ "$dir" != ".." ]] && [[ ! -L "$dir" ]]; then
            dir_array+=("$dir")
            dir_names+=("$(basename "$dir")")
            ((dir_index++))
        fi
    done < <(find . -maxdepth 1 -type d -not -name ".*" -print0 2>/dev/null | sort -zV)

    if [[ ${#dir_array[@]} -eq 0 ]]; then
        echo -e "${gl_huang}当前目录没有可下载的文件夹"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        exit_animation
        return 1
    fi

    for i in "${!dir_array[@]}"; do
        local idx=$((i + 1))
        local dir="${dir_array[$i]}"
        local dir_name="${dir_names[$i]}"
        local dir_size
        dir_size=$(du -sh "$dir" 2>/dev/null | cut -f1 || echo "未知")
        local file_count
        file_count=$(find "$dir" -type f 2>/dev/null | wc -l)

        printf "${gl_bufan}%3d. ${gl_bai}%-30s ${gl_huang}大小: %-8s ${gl_lv}文件数: %d\n" \
               "$idx" "$dir_name" "$dir_size" "$file_count"
    done

    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    read -r -e -p "$(echo -e "${gl_bai}请输入要下载的文件夹序号 (${gl_huang}0${gl_bai})返回: ")" dir_choice

    [ "$dir_choice" = "0" ] && { cancel_return "上一级选单"; return 1; }

    if ! [[ "$dir_choice" =~ ^[0-9]+$ ]] || \
       [[ "$dir_choice" -lt 1 ]] || \
       [[ "$dir_choice" -gt ${#dir_array[@]} ]]; then
        log_error "无效的文件夹序号"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        exit_animation
        return 1
    fi

    local selected_dir="${dir_array[$((dir_choice-1))]}"
    local dir_name="${dir_names[$((dir_choice-1))]}"
    local timestamp
    timestamp=$(date +%Y%m%d_%H%M%S)
    local zip_file="${dir_name}_${timestamp}"

    echo -e "${gl_bai}选择的文件夹: ${gl_huang}${dir_name}${gl_bai}"
    echo -e "${gl_bai}文件夹大小: ${gl_huang}$(du -sh "$selected_dir" 2>/dev/null | cut -f1)${gl_bai}"

    echo -e "${gl_huang}>>> 请选择压缩格式${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo -e "${gl_bufan}1.${gl_bai} tar.gz (推荐)"
    echo -e "${gl_bufan}2.${gl_bai} zip"
    echo -e "${gl_bufan}3.${gl_bai} tar.bz2"
    echo -e "${gl_bufan}4.${gl_bai} tar.xz"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    read -r -e -p "$(echo -e "${gl_bai}请输入你的选择 ${gl_bai}[${gl_lv}1${gl_bai}-${gl_huang}4${gl_bai}]: ")" format_choice

    case $format_choice in
        1)
            zip_file="${zip_file}.tar.gz"
            echo -e ""
            echo -e "${gl_huang}>>> 正在压缩为tar.gz格式 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            if tar -czf "$zip_file" "$dir_name" 2>/dev/null; then
                log_ok "文件夹压缩完成: ${gl_huang}$(du -h "$zip_file" 2>/dev/null | cut -f1)${gl_bai}"
                echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            else
                log_error "文件夹压缩失败"
                echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
                break_end
                return 1
            fi
            ;;
        2)
            zip_file="${zip_file}.zip"
            if command -v zip >/dev/null 2>&1; then
                echo -e ""
                echo -e "${gl_huang}>>> 正在压缩为zip格式 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
                echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
                if zip -rq "$zip_file" "$dir_name" 2>/dev/null; then
                    log_ok "文件夹压缩完成: ${gl_huang}$(du -h "$zip_file" 2>/dev/null | cut -f1)${gl_bai}"
                    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
                else
                    log_error "文件夹压缩失败"
                    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
                    break_end
                    return 1
                fi
            else
                log_error "未找到zip命令,使用tar.gz格式"
                zip_file="${dir_name}_${timestamp}.tar.gz"
                tar -czf "$zip_file" "$dir_name" 2>/dev/null
            fi
            ;;
        3)
            zip_file="${zip_file}.tar.bz2"
            echo -e ""
            echo -e "${gl_huang}>>> 正在压缩为tar.bz2格式 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            if tar -cjf "$zip_file" "$dir_name" 2>/dev/null; then
                log_ok "文件夹压缩完成: ${gl_huang}$(du -h "$zip_file" 2>/dev/null | cut -f1)${gl_bai}"
                echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            else
                log_error "文件夹压缩失败"
                echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
                break_end
                return 1
            fi
            ;;
        4)
            zip_file="${zip_file}.tar.xz"
            echo -e ""
            echo -e "${gl_huang}>>> 正在压缩为tar.xz格式 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            if tar -cJf "$zip_file" "$dir_name" 2>/dev/null; then
                log_ok "文件夹压缩完成: ${gl_huang}$(du -h "$zip_file" 2>/dev/null | cut -f1)${gl_bai}"
                echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            else
                log_error "文件夹压缩失败"
                echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
                break_end
                return 1
            fi
            ;;
        *)
            log_warn "无效选择,使用默认tar.gz格式"
            zip_file="${zip_file}.tar.gz"
            tar -czf "$zip_file" "$dir_name" 2>/dev/null
            ;;
    esac

    if [[ -f "$zip_file" ]]; then
        read -r -e -p "$(echo -e "${gl_bai}是否下载压缩包? (${gl_lv}y${gl_bai}/${gl_hong}n${gl_bai}): ")" download_choice
        if [[ "$download_choice" == "y" || "$download_choice" == "Y" ]]; then
            echo -e "${gl_bai}使用命令: ${gl_zi}sz \"${zip_file}\"${gl_bai}"
            echo -e "${gl_huang}请在本地客户端接收文件 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"

            for i in {2..1}; do
                echo -ne "${gl_huang}将在 ${gl_lv}$i${gl_bai} 秒后开始下载${gl_bai}\r"
                sleep_fractional 1
            done
            echo ""

            if sz "$zip_file"; then
                log_ok "文件夹下载成功!"
            else
                if [[ $? -eq 130 ]]; then
                    log_info "下载被用户取消"
                else
                    log_warn "文件传输可能被中断"
                fi
            fi
        fi

        read -r -e -p "$(echo -e "${gl_bai}是否删除临时压缩包? (${gl_lv}y${gl_bai}/${gl_hong}n${gl_bai}): ")" delete_choice
        if [[ "$delete_choice" == "y" || "$delete_choice" == "Y" ]]; then
            if rm -f "$zip_file" 2>/dev/null; then
                log_ok "已删除临时压缩包"
            else
                log_warn "删除临时压缩包失败"
            fi
        fi
    else
        log_error "压缩文件创建失败"
    fi
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

###### 选择文件下载
rz_download_files_interactive() {
    clear
    echo -e "${gl_zi}>>> 选择文件下载 (交互式)${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    local search_pattern
    read -r -e -p "$(echo -e "${gl_bai}请输入文件搜索模式 (如: *.txt, *.sh, 留空显示所有): ")" search_pattern

    if [[ -z "$search_pattern" ]]; then
        search_pattern="*"
    fi

    local file_list=()
    while IFS= read -r -d '' file; do
        file_list+=("$file")
    done < <(find . -maxdepth 1 -type f -name "$search_pattern" 2>/dev/null | sort -zV)

    if [[ ${#file_list[@]} -eq 0 ]]; then
        log_warn "未找到匹配的文件: ${gl_huang}${search_pattern}${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        exit_animation
        return 1
    fi

    echo -e "${gl_bai}找到以下文件:${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    local file_index=1
    local interactive_files=()

    for file in "${file_list[@]}"; do
        if [[ -f "$file" ]]; then
            local file_size
            file_size=$(stat -c%s "$file" 2>/dev/null || echo "0")
            local size_display

            if [[ $file_size -ge 1073741824 ]]; then
                size_display=$(echo "scale=2; $file_size/1073741824" | bc)G
            elif [[ $file_size -ge 1048576 ]]; then
                size_display=$(echo "scale=2; $file_size/1048576" | bc)M
            elif [[ $file_size -ge 1024 ]]; then
                size_display=$(echo "scale=2; $file_size/1024" | bc)K
            else
                size_display="${file_size}B"
            fi

            printf "${gl_bufan}%3d. ${gl_bai}%-40s ${gl_huang}%8s\n" \
                   "$file_index" "$(basename "$file")" "$size_display"
            interactive_files+=("$file")
            ((file_index++))
        fi
    done

    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    echo -e "${gl_bai}输入: ${gl_huang}单个序号${gl_bai}${gl_huang}多个序号用逗号分隔${gl_bai}${gl_huang}范围如1-5${gl_bai}"
    read -r -e -p "$(echo -e "${gl_bai}请选择要下载的文件 (${gl_huang}0${gl_bai})返回: ")" file_choices

    [ "$file_choices" = "0" ] && { cancel_return "上一级选单"; return 1; }

    if [[ -z "$file_choices" ]]; then
        log_error "请输入有效的文件序号"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        exit_animation
        return 1
    fi

    local selected_files=()

    if [[ "$file_choices" =~ ^[0-9]+$ ]]; then
        if [[ "$file_choices" -ge 1 && "$file_choices" -le ${#interactive_files[@]} ]]; then
            selected_files+=("${interactive_files[$((file_choices-1))]}")
        else
            log_error "无效的文件序号: $file_choices"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            exit_animation
            return 1
        fi
    elif [[ "$file_choices" =~ ^[0-9]+-[0-9]+$ ]]; then
        local start
        start=$(echo "$file_choices" | cut -d'-' -f1)
        local end
        end=$(echo "$file_choices" | cut -d'-' -f2)

        if [[ $start -ge 1 && $end -le ${#interactive_files[@]} && $start -le $end ]]; then
            for ((i=start; i<=end; i++)); do
                selected_files+=("${interactive_files[$((i-1))]}")
            done
        else
            log_error "无效的文件范围: $file_choices"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            exit_animation
            return 1
        fi
    else
        IFS=',' read -ra choices <<< "$file_choices"
        for choice in "${choices[@]}"; do
            choice=$(echo "$choice" | tr -d '[:space:]')
            if [[ "$choice" =~ ^[0-9]+$ ]] && \
               [[ "$choice" -ge 1 && "$choice" -le ${#interactive_files[@]} ]]; then
                selected_files+=("${interactive_files[$((choice-1))]}")
            fi
        done
    fi

    if [[ ${#selected_files[@]} -eq 0 ]]; then
        log_error "没有选择有效的文件"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        exit_animation
        return 1
    fi

    echo -e "${gl_bai}选择的文件:${gl_bai}"
    for file in "${selected_files[@]}"; do
        local file_size
        file_size=$(stat -c%s "$file" 2>/dev/null || echo "0")
        local size_display

        if [[ $file_size -ge 1073741824 ]]; then
            size_display=$(echo "scale=2; $file_size/1073741824" | bc)G
        elif [[ $file_size -ge 1048576 ]]; then
            size_display=$(echo "scale=2; $file_size/1048576" | bc)M
        elif [[ $file_size -ge 1024 ]]; then
            size_display=$(echo "scale=2; $file_size/1024" | bc)K
        else
            size_display="${file_size}B"
        fi

        echo -e "  ${gl_bufan}${gl_bai}$(basename "$file") (${gl_huang}$size_display${gl_bai})"
    done

    read -r -e -p "$(echo -e "${gl_bai}确认下载 ${gl_huang}${#selected_files[@]} ${gl_bai}个文件? (${gl_lv}y${gl_bai}/${gl_hong}n${gl_bai}): ")" confirm
    if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
        log_info "开始批量下载文件 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        echo -e "${gl_huang}请在本地客户端接收文件 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"

        for i in {2..1}; do
            echo -ne "${gl_huang}将在 ${gl_lv}$i${gl_bai} 秒后开始${gl_bai}\r"
            sleep_fractional 1
        done
        echo ""

        if sz "${selected_files[@]}"; then
            log_ok "批量下载完成!"
        else
            if [[ $? -eq 130 ]]; then
                log_info "下载被用户取消"
            else
                log_warn "批量下载可能被中断"
            fi
        fi
    else
        log_info "取消下载操作"
    fi
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

###### 批量上传多个文件
rz_upload_files_batch() {
    clear
    echo -e "${gl_zi}>>> 批量上传多个文件${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    log_info "可以一次选择多个文件上传"
    echo -e "${gl_bai}支持批量上传多个文件到当前目录${gl_bai}"
    echo -e "${gl_huang}注意: 请确保终端支持Zmodem批量上传${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    read -r -e -p "$(echo -e "${gl_bai}是否继续? (${gl_lv}y${gl_bai}/${gl_hong}n${gl_bai}): ")" confirm
    if [[ "$confirm" == "y" || "$confirm" == "Y" ]]; then
        echo -e "${gl_bai}使用命令: ${gl_zi}rz -bye${gl_bai}"
        echo -e "${gl_huang}请在本地客户端选择多个文件 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
        echo -e "${gl_huang}提示: 按${gl_bai}Ctrl+C${gl_huang}取消上传${gl_bai}"

        for i in {2..1}; do
            echo -ne "${gl_huang}将在 ${gl_lv}$i${gl_bai} 秒后弹出文件选择对话框${gl_bai}\r"
            sleep_fractional 1
        done
        echo ""

        if command -v rz >/dev/null 2>&1; then
            if rz -bye 2>/dev/null; then
                log_ok "批量上传完成!"

                echo -e "${gl_bai}最近上传的文件:${gl_bai}"
                ls -lt --color=always 2>/dev/null | head -6
            else
                if [[ $? -eq 130 ]]; then
                    log_info "上传被用户取消"
                else
                    log_warn "批量上传失败,尝试普通上传模式 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
                    if rz; then
                        log_ok "文件上传完成!"
                    else
                        log_warn "文件上传可能被取消"
                    fi
                fi
            fi
        else
            log_error "rz命令不可用"
        fi
    fi
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

###### 检查终端Zmodem支持
rz_check_zmodem_support() {
    clear
    echo -e "${gl_zi}>>> 检查终端Zmodem支持${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    echo -e "${gl_bai}Zmodem协议支持检测:${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    if command -v sz >/dev/null 2>&1 && command -v rz >/dev/null 2>&1; then
        echo -e "  ${gl_lv}${gl_bai}lrzsz已安装: ${gl_huang}$(sz --version 2>&1 | head -1)${gl_bai}"
    else
        echo -e "  ${gl_hong}${gl_bai}lrzsz未安装"
    fi

    if [[ -n "$SSH_TTY" ]]; then
        echo -e "  ${gl_lv}${gl_bai}通过SSH连接${gl_bai}"
    else
        echo -e "  ${gl_huang}${gl_bai}非SSH连接,Zmodem可能不可用${gl_bai}"
    fi

    echo -e "${gl_bai}支持的终端软件:${gl_bai}"
    echo -e "  ${gl_bufan}${gl_lv}MobaXterm${gl_bai} - 内置完美支持"
    echo -e "  ${gl_bufan}${gl_lv}Xshell${gl_bai} - 需要启用ZMODEM选项"
    echo -e "  ${gl_bufan}${gl_lv}SecureCRT${gl_bai} - 需要配置ZMODEM"
    echo -e "  ${gl_bufan}${gl_lv}Tabby${gl_bai} - 需要安装Zmodem插件"
    echo -e "  ${gl_bufan}${gl_lv}WindTerm${gl_bai} - 内置支持"
    echo -e "  ${gl_bufan}${gl_lv}FinalShell${gl_bai} - 内置支持"
    echo -e "  ${gl_huang}${gl_bai}PuTTY${gl_bai} - 不支持Zmodem协议"
    echo -e "  ${gl_huang}${gl_bai}Windows Terminal${gl_bai} - 需要额外配置"

    echo -e "${gl_bai}快速测试:${gl_bai}"
    echo -e "  ${gl_bufan}1. ${gl_bai}上传测试: ${gl_zi}echo 'test' > test.txt && sz test.txt${gl_bai}"
    echo -e "  ${gl_bufan}2. ${gl_bai}下载测试: ${gl_zi}rz (然后选择文件)${gl_bai}"

    echo -e "${gl_bai}其他传输方式:${gl_bai}"
    echo -e "  ${gl_bufan}${gl_bai}SCP: ${gl_zi}scp user@host:file .${gl_bai}"
    echo -e "  ${gl_bufan}${gl_bai}SFTP: ${gl_zi}sftp user@host${gl_bai}"
    echo -e "  ${gl_bufan}${gl_bai}HTTP: ${gl_zi}python3 -m http.server${gl_bai}"

    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    read -r -e -p "$(echo -e "${gl_bai}是否创建测试文件? (${gl_lv}y${gl_bai}/${gl_hong}n${gl_bai}): ")" test_choice
    if [[ "$test_choice" == "y" || "$test_choice" == "Y" ]]; then
        echo "这是一个Zmodem传输测试文件,创建于: $(date)" > zmodem_test.txt
        echo "文件大小: 1KB" >> zmodem_test.txt
        echo "用于测试Zmodem文件传输功能" >> zmodem_test.txt
        dd if=/dev/zero bs=1k count=1 2>/dev/null >> zmodem_test.txt
        log_ok "测试文件已创建: ${gl_huang}zmodem_test.txt${gl_bai}"
        echo -e "${gl_bai}使用命令测试: ${gl_zi}sz zmodem_test.txt${gl_bai}"
    fi

    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

###### 查看传输历史记录
rz_view_transfer_history() {
    clear
    echo -e "${gl_zi}>>> 查看传输历史记录${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    echo -e "${gl_bai}最近修改的文件 (可能是上传的文件):${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    find . -maxdepth 1 -type f -printf "%T+ %p\n" 2>/dev/null | sort -r | head -10 | while read -r line; do
        local file_date file_name
        file_date=$(echo "$line" | awk '{print $1" "$2}' | cut -d'.' -f1)
        file_name=$(echo "$line" | awk '{for(i=2;i<=NF;i++) printf $i" "}' | sed 's/ $//')
        local file_size
        file_size=$(stat -c%s "$file_name" 2>/dev/null || echo "0")
        local size_display

        if [[ $file_size -ge 1073741824 ]]; then
            size_display=$(echo "scale=2; $file_size/1073741824" | bc)G
        elif [[ $file_size -ge 1048576 ]]; then
            size_display=$(echo "scale=2; $file_size/1048576" | bc)M
        elif [[ $file_size -ge 1024 ]]; then
            size_display=$(echo "scale=2; $file_size/1024" | bc)K
        else
            size_display="${file_size}B"
        fi

        printf "${gl_bai}%-20s ${gl_huang}%8s ${gl_lv}%s\n" "$file_date" "$size_display" "$(basename "$file_name")"
    done

    echo -e "${gl_bai}当前目录中大文件 (>10MB):${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    find . -maxdepth 1 -type f -size +10M 2>/dev/null | while read -r file; do
        local file_size
        file_size=$(stat -c%s "$file" 2>/dev/null || echo "0")
        local size_display

        if [[ $file_size -ge 1073741824 ]]; then
            size_display=$(echo "scale=2; $file_size/1073741824" | bc)G
        elif [[ $file_size -ge 1048576 ]]; then
            size_display=$(echo "scale=2; $file_size/1048576" | bc)M
        fi

        printf "${gl_bai}%-40s ${gl_huang}%8s\n" "$(basename "$file")" "$size_display"
    done | head -5

    local total_size
    total_size=$(find . -maxdepth 1 -type f -exec stat -c%s {} \; 2>/dev/null | awk '{sum+=$1} END {print sum}')
    local total_files
    total_files=$(find . -maxdepth 1 -type f 2>/dev/null | wc -l)
    local total_size_display

    if [[ $total_size -ge 1073741824 ]]; then
        total_size_display=$(echo "scale=2; $total_size/1073741824" | bc)GB
    elif [[ $total_size -ge 1048576 ]]; then
        total_size_display=$(echo "scale=2; $total_size/1048576" | bc)MB
    elif [[ $total_size -ge 1024 ]]; then
        total_size_display=$(echo "scale=2; $total_size/1024" | bc)KB
    else
        total_size_display="${total_size}B"
    fi

    echo -e "${gl_bai}统计信息:${gl_bai}"
    echo -e "  ${gl_bufan}${gl_bai}文件总数: ${gl_huang}${total_files}${gl_bai}"
    echo -e "  ${gl_bufan}${gl_bai}总大小: ${gl_huang}${total_size_display}${gl_bai}"

    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    read -r -e -p "$(echo -e "${gl_bai}按回车键继续 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}")"
    break_end
}

###### 清理临时文件
rz_clean_temp_files() {
    clear
    echo -e "${gl_zi}>>> 清理临时文件${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    echo -e "${gl_bai}正在查找临时文件 ${gl_hong}.${gl_huang}.${gl_lv}.${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    local temp_files=()
    while IFS= read -r -d '' file; do
        temp_files+=("$file")
    done < <(find . -maxdepth 1 -type f \( -name "*.tmp" -o -name "*.temp" -o -name "*.swp" -o -name "*.swo" -o -name "*~" -o -name ".#*" \) -print0 2>/dev/null)

    if [[ ${#temp_files[@]} -gt 0 ]]; then
        echo -e "${gl_bai}找到以下临时文件:${gl_bai}"
        for file in "${temp_files[@]}"; do
            local file_size
            file_size=$(stat -c%s "$file" 2>/dev/null || echo "0")
            local size_display

            if [[ $file_size -ge 1048576 ]]; then
                size_display=$(echo "scale=2; $file_size/1048576" | bc)M
            elif [[ $file_size -ge 1024 ]]; then
                size_display=$(echo "scale=2; $file_size/1024" | bc)K
            else
                size_display="${file_size}B"
            fi

            printf "  ${gl_bufan}${gl_bai}%-40s ${gl_huang}%8s\n" "$(basename "$file")" "$size_display"
        done

        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        read -r -e -p "$(echo -e "${gl_bai}是否删除这些临时文件? (${gl_lv}y${gl_bai}/${gl_hong}n${gl_bai}): ")" delete_choice

        if [[ "$delete_choice" == "y" || "$delete_choice" == "Y" ]]; then
            local deleted_count=0
            local deleted_size=0

            for file in "${temp_files[@]}"; do
                local file_size
                file_size=$(stat -c%s "$file" 2>/dev/null || echo "0")
                if rm -f "$file" 2>/dev/null; then
                    ((deleted_count++))
                    deleted_size=$((deleted_size + file_size))
                fi
            done

            local deleted_size_display
            if [[ $deleted_size -ge 1048576 ]]; then
                deleted_size_display=$(echo "scale=2; $deleted_size/1048576" | bc)MB
            elif [[ $deleted_size -ge 1024 ]]; then
                deleted_size_display=$(echo "scale=2; $deleted_size/1024" | bc)KB
            else
                deleted_size_display="${deleted_size}B"
            fi

            log_ok "已删除 ${gl_huang}${deleted_count}${gl_bai} 个临时文件,释放空间: ${gl_huang}${deleted_size_display}${gl_bai}"
        else
            log_info "取消删除临时文件"
        fi
    else
        log_info "未找到临时文件"
    fi
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

###### 创建测试文件
rz_create_test_files() {
    clear
    echo -e "${gl_zi}>>> 创建测试文件${gl_bai}"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    echo -e "${gl_bai}创建测试文件用于测试Zmodem传输:${gl_bai}"
    echo -e "${gl_bufan}1. ${gl_bai}创建小文件 (1KB)"
    echo -e "${gl_bufan}2. ${gl_bai}创建中文件 (1MB)"
    echo -e "${gl_bufan}3. ${gl_bai}创建大文件 (10MB)"
    echo -e "${gl_bufan}4. ${gl_bai}创建随机文件"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    read -r -e -p "$(echo -e "${gl_bai}请选择 [1-4]: ")" test_choice

    case $test_choice in
        1)
            echo "Zmodem测试文件 - 小文件 (1KB)" > test_small.txt
            echo "创建时间: $(date)" >> test_small.txt
            dd if=/dev/zero bs=1k count=1 2>/dev/null >> test_small.txt
            log_ok "已创建测试文件: ${gl_huang}test_small.txt${gl_bai} (1KB)"
            ;;
        2)
            echo "Zmodem测试文件 - 中文件 (1MB)" > test_medium.txt
            echo "创建时间: $(date)" >> test_medium.txt
            dd if=/dev/zero bs=1M count=1 2>/dev/null >> test_medium.txt
            log_ok "已创建测试文件: ${gl_huang}test_medium.txt${gl_bai} (1MB)"
            ;;
        3)
            echo "Zmodem测试文件 - 大文件 (10MB)" > test_large.txt
            echo "创建时间: $(date)" >> test_large.txt
            dd if=/dev/zero bs=1M count=10 2>/dev/null >> test_large.txt
            log_ok "已创建测试文件: ${gl_huang}test_large.txt${gl_bai} (10MB)"
            ;;
        4)
            read -r -e -p "$(echo -e "${gl_bai}请输入文件大小 (如: 1K, 1M, 10M): ")" size
            echo "Zmodem测试文件 - 随机文件 (${size})" > "test_random_${size}.txt"
            echo "创建时间: $(date)" >> "test_random_${size}.txt"

            if dd if=/dev/urandom bs="${size}" count=1 2>/dev/null >> "test_random_${size}.txt"; then
                log_ok "已创建测试文件: ${gl_huang}test_random_${size}.txt${gl_bai} (${size})"
            else
                log_error "创建文件失败,请检查输入格式 (如: 1K, 1M, 10M)"
            fi
            ;;
        0) cancel_return; return ;;
        *) handle_invalid_input ;;
    esac

    if [[ -f "test_"*".txt" ]]; then
        echo -e "${gl_bai}测试文件信息:${gl_bai}"
        ls -lh test_*.txt 2>/dev/null

        read -r -e -p "$(echo -e "${gl_bai}是否立即测试下载? (${gl_lv}y${gl_bai}/${gl_hong}n${gl_bai}): ")" download_test
        if [[ "$download_test" == "y" || "$download_test" == "Y" ]]; then
            for i in {2..1}; do
                echo -ne "${gl_huang}将在 ${gl_lv}$i${gl_bai} 秒后开始下载测试文件${gl_bai}\r"
                sleep_fractional 1
            done
            echo ""

            sz test_*.txt
        fi
    fi
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
    break_end
}

list_dir_colorful() {
    local show_hidden="${1:-0}"
    local user_cols="${2:-0}"
    local files=()
    local has_content=0
    local item

    declare -A type_color=(
        [dir]="${gl_bufan}"
        [exe]="${gl_lv}"
        [link]="${gl_zi}"
        [archive]="${gl_hong}"
        [image]="${gl_huang}"
        [code]="${gl_lan}"
        [text]="${gl_bai}"
        [else]="${gl_hui}"
    )

    if [[ "${show_hidden}" -eq 1 ]]; then
        while IFS= read -r item; do
            [[ -e "${item}" || -L "${item}" ]] && {
                files+=("${item}")
                has_content=1
            }
        done < <(ls -A 2>/dev/null)
    else
        for item in *; do
            [[ -e "${item}" || -L "${item}" ]] && {
                files+=("${item}")
                has_content=1
            }
        done 2>/dev/null
    fi

    echo -e "${gl_huang}>>> 当前目录文件列表:${gl_bai}(${gl_lv}$(pwd)${gl_bai})"
    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    if [[ ${has_content} -eq 0 ]]; then
        echo -e "${gl_huang}当前目录为空${gl_bai}"
        return 0
    fi

    local file_info=()
    local max_display_width=0

    for item in "${files[@]}"; do
        local color="" suffix=""

        if [[ -L "${item}" ]]; then
            color="${type_color[link]}"
            suffix="@"
        elif [[ -d "${item}" ]]; then
            color="${type_color[dir]}"
            suffix="/"
        elif [[ -x "${item}" ]]; then
            color="${type_color[exe]}"
            suffix="*"
        else
            local ext="${item##*.}"
            if [[ "${ext}" != "${item}" ]]; then
                case "${ext,,}" in
                    tar|gz|bz2|xz|zip|7z|rar|zst|tgz|tbz2|txz)
                        color="${type_color[archive]}" ;;
                    jpg|jpeg|png|gif|bmp|webp|svg|ico|tiff|avif)
                        color="${type_color[image]}" ;;
                    sh|py|pl|rb|go|cpp|c|h|hpp|js|ts|jsx|tsx|java|php|rs|swift|kt|lua|vim)
                        color="${type_color[code]}" ;;
                    txt|md|log|conf|cfg|yml|yaml|json|xml|ini|csv|toml)
                        color="${type_color[text]}" ;;
                    *)
                        color="${type_color[else]}" ;;
                esac
            else
                if [[ -b "${item}" || -c "${item}" ]]; then
                    color="${type_color[else]}"
                else
                    color="${type_color[text]}"
                fi
            fi
        fi

        local display_str="${item}${suffix}"
        local display_width

        if command -v wc &>/dev/null; then
            display_width=$(printf "%s" "${display_str}" | wc -L 2>/dev/null || echo "${#display_str}")
        else
            display_width=${#display_str}
        fi

        (( display_width > max_display_width )) && max_display_width=${display_width}

        file_info+=("${item}" "${color}" "${suffix}" "${display_width}" "${display_str}")
    done

    local term_width
    term_width=$(tput cols 2>/dev/null || echo 80)

    local col_width=$((max_display_width + 4))

    local items_per_line
    if [[ ${user_cols} -gt 0 ]]; then
        items_per_line=${user_cols}
        local needed_width=$((items_per_line * col_width - 4))
        if (( needed_width > term_width )); then
            items_per_line=$(( (term_width + 4) / col_width ))
            (( items_per_line < 1 )) && items_per_line=1
        fi
    else
        items_per_line=$((term_width / col_width))
        (( items_per_line < 1 )) && items_per_line=1
    fi

    (( items_per_line > ${#files[@]} )) && items_per_line=${#files[@]}

    local total_items=${#files[@]}
    local rows=$(( (total_items + items_per_line - 1) / items_per_line ))

    local row col index idx_base file_name file_color file_suffix file_width padding

    for ((row = 0; row < rows; row++)); do
        for ((col = 0; col < items_per_line; col++)); do
            index=$((row * items_per_line + col))

            if ((index < total_items)); then
                idx_base=$((index * 5))
                file_name="${file_info[idx_base]}"
                file_color="${file_info[idx_base+1]}"
                file_suffix="${file_info[idx_base+2]}"
                file_width="${file_info[idx_base+3]}"

                padding=$((col_width - file_width))

                printf "%b%s%b" "${file_color}" "${file_name}${file_suffix}" "${gl_bai}"

                if ((col < items_per_line - 1 && index < total_items - 1)); then
                    printf "%*s" "${padding}" ""
                fi
            fi
        done
        echo
    done

    echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

    local total=${#files[@]}
    local dir_count=0 file_count=0 link_count=0
    for item in "${files[@]}"; do
        if [[ -L "${item}" ]]; then
            ((link_count++))
        elif [[ -d "${item}" ]]; then
            ((dir_count++))
        else
            ((file_count++))
        fi
    done

    echo -e "${gl_bai}总计: ${gl_lv}${total}${gl_bai}${gl_bufan}目录: ${dir_count}${gl_bai}    文件: ${file_count}${gl_bai}    ${gl_zi}链接: ${link_count}${gl_bai}"

    if [[ ${user_cols} -gt 0 ]]; then
        echo -e "${gl_hui}布局: ${gl_lv}${rows}${gl_hui}${gl_huang}× ${gl_lv}${items_per_line}${gl_hui}${gl_bai}"
    else
        echo -e "${gl_hui}布局: ${gl_lv}${rows}${gl_hui}${gl_huang}× ${gl_lv}${items_per_line}${gl_hui} 列 (${gl_huang}自动计算${gl_hui})${gl_bai}"
    fi

    return 0
}

file_transfer_manager() {
    while true; do
        install lrzsz bc
        clear
        if [ -z "$(ls -A 2>/dev/null)" ]; then
            echo -e "${gl_huang}>>> 当前目录文件列表:${gl_bai}(${gl_lv}$(pwd)${gl_bai})"
            echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
            echo -e "${gl_huang}当前目录为空${gl_bai}"
        else
            list_dir_colorful 0 4
        fi
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e ""
        echo -e "${gl_zi}>>> 文件传输管理器 (Zmodem)${gl_bai}"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_bufan}1.  ${gl_bai}上传文件到服务器 ${gl_huang}${gl_bai}    ${gl_bufan}2.  ${gl_bai}下载文件到本地 ${gl_huang}${gl_bai}"
        echo -e "${gl_bufan}3.  ${gl_bai}选择文件下载          ${gl_bufan}4.  ${gl_bai}批量上传多个文件"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_bufan}5.  ${gl_bai}上传文件夹${gl_huang}压缩包${gl_bai}      ${gl_bufan}6.  ${gl_bai}下载文件夹${gl_huang}压缩包${gl_bai}"
        echo -e "${gl_bufan}7.  ${gl_bai}创建测试文件          ${gl_bufan}8.  ${gl_bai}清理临时文件"
        echo -e "${gl_bufan}9.  ${gl_bai}检查终端Zmodem支持    ${gl_bufan}10. ${gl_bai}查看传输历史记录"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"
        echo -e "${gl_hong}0.  ${gl_bai}退出脚本"
        echo -e "${gl_bufan}————————————————————————————————————————————————${gl_bai}"

        read -r -e -p "$(echo -e "${gl_bai}请输入你的选择: ")" choice

        case $choice in
        1)  rz_upload_file; continue ;;
        2)  rz_download_files_to_local; continue ;;
        3)  rz_download_files_interactive; continue ;;
        4)  rz_upload_files_batch; continue ;;
        5)  rz_upload_compressed_file; continue ;;
        6)  rz_download_folder; continue ;;
        7)  rz_create_test_files; continue ;;
        8)  rz_clean_temp_files; continue ;;
        9)  rz_check_zmodem_support; continue ;;
        10) rz_view_transfer_history; continue ;;
        0)  exit_script ;;
        *) handle_invalid_input ;;
        esac
    done
}

file_transfer_manager

相关命令