我一开始连 WebDAV 和 SMB 都分不清,最后却用 systemd + shell 脚本让 USB 机械硬盘自动休眠


🌱 起点:我该用 WebDAV 还是 SMB?

几个月前,我想把家里的旧电脑变成一台简单的文件服务器,让手机、笔记本、树莓派都能随时访问里面的电影和备份。

我在网上看到两个词反复出现:WebDAVSMB
但它们到底有什么区别?哪个更适合我?

  • WebDAV:基于 HTTP 的协议,配置简单,防火墙友好(走 80/443 端口),适合外网访问。
  • SMB(Samba):Windows 原生支持,局域网内速度更快,兼容性极好,但外网穿透稍复杂。

我当时想:"既然 WebDAV 能走 HTTPS,是不是更安全、更现代?"
于是,我决定先试试 WebDAV。


🔧 第一步:用 OpenList Desktop 挂载 WebDAV 为 Windows 本地磁盘

我的技术栈是这样的:

  • 服务器端:在 Ubuntu 上使用 lucky 开启 WebDAV 功能
  • 客户端:在 Windows 上使用 OpenList Desktop 将 WebDAV 挂载为本地磁盘(而不是网络映射驱动器)

OpenList Desktop 提供了图形化界面,让配置变得非常简单:

  • 下载并安装应用程序
  • 在"挂载管理"中添加云存储
  • 配置 WebDAV 端点 URL、用户名和密码
  • 设置本地挂载点路径(如 Z:
  • 点击"保存"和"挂载"按钮

挂载成功!我能在 Windows 文件管理器里看到它作为一个本地磁盘出现,就像真正的硬盘一样。 一切看起来都很美好……直到我想运行里面的程序。


⚠️ 转折点:WebDAV 挂载的磁盘不能运行程序!

某天,我尝试在挂载的 WebDAV "磁盘"里执行一个程序:

myprogram.exe

结果报错:

直接报错了不给运行,这他*问题怎么这么多?

查资料才发现:WebDAV 本质上是 HTTP 文件传输协议,不支持文件锁、执行权限、硬链接等本地文件系统特性。 即使文件看起来在 Windows 中是可执行文件,系统也无法真正"执行"它——因为底层是通过 HTTP 协议传输文件,没有提供本地文件系统的执行语义。

更糟的是,某些应用(如数据库、Docker、甚至部分 IDE)会直接崩溃,因为它们依赖本地文件系统的完整行为。

💡 教训:WebDAV 适合只读或文档同步不适合运行程序或作为工作目录


🔄 决定转向:拥抱 SMB

既然 WebDAV 有这些硬伤,而我的主要使用场景是家庭局域网(手机、PC、树莓派都在同一个 WiFi 下),那为什么不试试 SMB?

SMB 是 Windows 的"网上邻居"协议,Linux 用 Samba 实现。它:

  • 支持完整文件权限和执行位
  • 局域网速度接近本地硬盘
  • 手机(用 ES 文件浏览器等)和 Windows/Mac 都原生支持

我很快在服务器上装了 Samba:

sudo apt install samba

配置 /etc/samba/smb.conf,添加共享目录,设置用户名密码……
几分钟后,我的 Windows 电脑就能通过 \\192.168.1.100\backup 访问文件了!
树莓派上用 mount -t cifs 也能完美挂载。

问题解决了?不,新的挑战来了……


💾 新问题:USB 机械硬盘 24 小时不休眠,又吵又耗电!

我的 Samba 共享目录,其实是挂载在两块 USB 机械硬盘 上的。
这些硬盘一旦通电就一直高速旋转,即使没人访问——嗡嗡声吵得睡不着,还白白耗电

我查到可以用 hdparm 命令让硬盘休眠:

sudo hdparm -S 242 /dev/sdb  # 60 分钟无操作自动休眠

手动执行有效!但每次重启后设置就失效了
而且,我的硬盘设备名不固定(有时是 sdb/sdc,有时是 sdd/sde),写死路径不靠谱。


🛠️ 终极方案:写一个智能脚本 + systemd 服务

我向 AI 助手求助,得到了一个绝妙的思路:

自动识别所有 USB 接口的机械硬盘,统一设置休眠策略

于是,我们一起打磨出这个脚本:

✅ 核心逻辑

  1. lsblk -d -o NAME,ROTA,TRAN,TYPE 扫描所有磁盘
  2. 筛选出:TYPE=disk + ROTA=1(机械盘)+ TRAN=usb
  3. 对每个设备执行 hdparm -S 242(60分钟休眠)

📜 脚本:/home/start_run/hdparm-usb-hdd-setup.sh

(完整代码见文末)

它会:

  • 自动发现设备,无需硬编码
  • 带日志记录(/var/log/hdparm-usb-hdd-setup.log
  • 超时保护,防止卡死
  • 等待系统启动后运行(10秒延迟)
  • 开机自动运行

🔌 配套 systemd 服务

[Unit]
Description=为所有USB机械硬盘设置60分钟自动休眠
After=multi-user.target
After=systemd-udev-settle.service

[Service]
Type=oneshot
ExecStart=/home/start_run/hdparm-usb-hdd-setup.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

启用后:

sudo systemctl enable --now hdparm-usb-hdd.service

从此,每次开机,所有 USB 机械硬盘都会自动进入"60 分钟无操作即休眠"模式
安静了,省电了,硬盘寿命也延长了!


📝 总结:我的技术成长路径

阶段 我的认知 解决方案
起初 "WebDAV 和 SMB 有啥区别?" 查资料,动手试
遇坑 "为什么挂载的盘不能运行程序?" 理解协议底层限制
转向 "SMB 更适合我的局域网场景" 部署 Samba
新问题 "USB 硬盘一直转,太吵!" 学习 hdparm
终极方案 "如何自动化、通用化?" 编写智能 shell 脚本 + systemd

💡 给后来者的建议

  • WebDAV ≠ 本地磁盘:别指望它能完美替代本地存储。
  • SMB 是家庭局域网的王者:简单、快、兼容好。
  • 机械硬盘记得设休眠:省电、静音、延寿,三赢。
  • 脚本要智能:别写死设备名,用 lsblk + ROTA 自动识别。
  • 使用自动化安装:我们提供了完整的安装脚本,一键部署。

📎 附录:完整脚本代码

主脚本:hdparm-usb-hdd-setup.sh

#!/bin/bash
# 自动为所有 USB 机械硬盘设置 30 分钟自动休眠
# 作者: xiaotian
# 日期: 2025-10-11

set -euo pipefail

# 全局配置
LOGFILE="/var/log/hdparm-usb-hdd-setup.log"
SPINDOWN_VALUE=242   # 2分钟 (24 = 2m, 241 = 30m, 242 = 60m, ...)

# -----------------------------------------------------------------------------
# 日志记录函数
# -----------------------------------------------------------------------------
log() {
    local msg="$*"
    local log_dir
    log_dir=$(dirname "$LOGFILE")
    
    # 确保日志目录存在
    if [[ ! -d "$log_dir" ]]; then
        mkdir -p "$log_dir" 2>/dev/null || {
            echo "[$(date +'%Y-%m-%d %H:%M:%S')] $msg"
            return
        }
    fi
    
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $msg" | tee -a "$LOGFILE"
}

# -----------------------------------------------------------------------------
# 获取所有 USB 旋转设备(机械硬盘)
# 输出: 每行一个设备名,如 sdb sdc
# -----------------------------------------------------------------------------
get_usb_rotational_drives() {
    # 使用 lsblk -d (仅磁盘), -n (无表头), -o (输出字段)
    # 过滤: ROTA=1 (旋转设备), TRAN=usb (USB接口)
    lsblk -d -n -o NAME,ROTA,TRAN,TYPE | \
    while read -r name rota tran type _; do
        # 只处理磁盘设备(TYPE=disk),排除分区
        if [[ "$type" == "disk" ]] && [[ "$rota" == "1" ]] && [[ "$tran" == "usb" ]]; then
            echo "$name"
        fi
    done
}

# -----------------------------------------------------------------------------
# 设置单个设备的自动休眠
# 参数: $1 = 设备名 (如 "sdb")
# -----------------------------------------------------------------------------
set_spindown_for_device() {
    local dev_name="$1"
    local dev_path="/dev/$dev_name"

    if [[ ! -b "$dev_path" ]]; then
        log "警告: 设备 $dev_path 不存在,跳过"
        return 1
    fi

    log "正在设置 $dev_path 的自动休眠..."

    # 使用 timeout 防止 hdparm 卡死(某些 USB 桥接芯片无响应)
    if timeout 5 hdparm -S "$SPINDOWN_VALUE" "$dev_path" &>/dev/null; then
        log "成功: $dev_path 已设置 60 分钟自动休眠"
        
        # 验证设置是否生效
        if timeout 3 hdparm -C "$dev_path" &>/dev/null; then
            log "验证: $dev_path 休眠功能正常"
        fi
        return 0
    else
        local model
        model=$(lsblk -no MODEL "$dev_path" 2>/dev/null || echo "未知")
        log "失败: $dev_path 不支持 hdparm -S(型号: $model)"
        return 1
    fi
}

# -----------------------------------------------------------------------------
# 主函数
# -----------------------------------------------------------------------------
main() {
    log "=== 开始扫描并配置 USB 机械硬盘 ==="
    
    # 等待系统启动
    sleep 10

    local devices
    mapfile -t devices < <(get_usb_rotational_drives)

    if [[ ${#devices[@]} -eq 0 ]]; then
        log "未发现任何 USB 机械硬盘"
        return 0
    fi

    local dev
    for dev in "${devices[@]}"; do
        set_spindown_for_device "$dev" || true  # 继续处理其他设备
    done

    log "=== 配置完成 ==="
}

# -----------------------------------------------------------------------------
# 脚本入口
# -----------------------------------------------------------------------------
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
fi

安装脚本:install.sh

我们还提供了一个完整的安装脚本,可以一键部署所有组件:

#!/bin/bash
# USB机械硬盘自动休眠安装脚本
# 作者: xiaotian
# 日期: 2025-10-11

set -euo pipefail

# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 日志函数
log_info() {
    echo -e "${BLUE}[信息]${NC} $*"
}

log_success() {
    echo -e "${GREEN}[成功]${NC} $*"
}

log_warning() {
    echo -e "${YELLOW}[警告]${NC} $*"
}

log_error() {
    echo -e "${RED}[错误]${NC} $*"
}

# 检查root权限
check_root() {
    if [[ $EUID -ne 0 ]]; then
        log_error "此脚本需要root权限运行"
        log_info "请使用: sudo $0"
        exit 1
    fi
}

# 检查依赖
check_dependencies() {
    log_info "检查系统依赖..."
    
    local missing_deps=()
    
    # 检查hdparm
    if ! command -v hdparm &> /dev/null; then
        missing_deps+=("hdparm")
    fi
    
    # 检查lsblk
    if ! command -v lsblk &> /dev/null; then
        missing_deps+=("util-linux (lsblk)")
    fi
    
    # 检查timeout
    if ! command -v timeout &> /dev/null; then
        missing_deps+=("coreutils (timeout)")
    fi
    
    if [[ ${#missing_deps[@]} -gt 0 ]]; then
        log_error "缺少必要的依赖包: ${missing_deps[*]}"
        log_info "请使用以下命令安装:"
        log_info "Ubuntu/Debian: apt-get install ${missing_deps[0]}"
        log_info "CentOS/RHEL: yum install ${missing_deps[0]}"
        exit 1
    fi
    
    log_success "所有依赖检查通过"
}

# 安装主脚本
install_main_script() {
    log_info "安装主脚本..."
    
    local script_path="/home/start_run/hdparm-usb-hdd-setup.sh"
    
    # 备份原有文件
    if [[ -f "$script_path" ]]; then
        local backup_path="${script_path}.backup.$(date +%Y%m%d_%H%M%S)"
        cp "$script_path" "$backup_path"
        log_info "已备份原有脚本: $backup_path"
    fi
    
    # 复制新脚本
    cp hdparm-usb-hdd-setup.sh "$script_path"
    chmod +x "$script_path"
    
    log_success "主脚本已安装到: $script_path"
}

# 安装服务文件
install_service() {
    log_info "安装systemd服务..."
    
    local service_path="/etc/systemd/system/hdparm-usb-hdd.service"
    
    # 备份原有服务文件
    if [[ -f "$service_path" ]]; then
        local backup_path="${service_path}.backup.$(date +%Y%m%d_%H%M%S)"
        cp "$service_path" "$backup_path"
        log_info "已备份原有服务文件: $backup_path"
    fi
    
    # 复制服务文件
    cp hdparm-usb-hdd.service "$service_path"
    
    log_success "服务文件已安装到: $service_path"
}

# 配置系统服务
configure_service() {
    log_info "配置系统服务..."
    
    # 重新加载systemd
    systemctl daemon-reload
    
    # 启用服务
    systemctl enable hdparm-usb-hdd.service
    
    # 启动服务
    if systemctl start hdparm-usb-hdd.service; then
        log_success "服务启动成功"
    else
        log_error "服务启动失败"
        log_info "请检查服务状态: systemctl status hdparm-usb-hdd.service"
        return 1
    fi
}

# 测试脚本功能
test_script() {
    log_info "测试脚本功能..."
    
    if /home/start_run/hdparm-usb-hdd-setup.sh; then
        log_success "脚本测试通过"
    else
        log_warning "脚本测试遇到问题,但安装继续"
    fi
}

# 显示安装摘要
show_summary() {
    echo
    log_success "=== 安装完成 ==="
    log_info "服务名称: hdparm-usb-hdd.service"
    log_info "脚本位置: /home/start_run/hdparm-usb-hdd-setup.sh"
    log_info "日志文件: /var/log/hdparm-usb-hdd-setup.log"
    echo
    log_info "常用命令:"
    log_info "  查看服务状态: systemctl status hdparm-usb-hdd.service"
    log_info "  查看服务日志: journalctl -u hdparm-usb-hdd.service"
    log_info "  手动运行脚本: /home/start_run/hdparm-usb-hdd-setup.sh"
    log_info "  重启服务: systemctl restart hdparm-usb-hdd.service"
    echo
    log_info "服务将在系统启动时自动运行,为所有USB机械硬盘设置60分钟自动休眠"
}

# 主安装函数
main() {
    log_info "开始安装USB机械硬盘自动休眠服务..."
    
    check_root
    check_dependencies
    install_main_script
    install_service
    configure_service
    test_script
    show_summary
    
    log_success "安装完成!"
}

# 脚本入口
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    main "$@"
fi

使用方法:

# 下载所有文件后,运行安装脚本
sudo ./install.sh

现在,我的小服务器安静地运行着,硬盘只在需要时醒来——这,就是极客的幸福。 😌