从 WebDAV 到 SMB:一个 Linux 新手的局域网存储挂载探索之旅
我一开始连 WebDAV 和 SMB 都分不清,最后却用 systemd + shell 脚本让 USB 机械硬盘自动休眠
🌱 起点:我该用 WebDAV 还是 SMB?
几个月前,我想把家里的旧电脑变成一台简单的文件服务器,让手机、笔记本、树莓派都能随时访问里面的电影和备份。
我在网上看到两个词反复出现:WebDAV 和 SMB。
但它们到底有什么区别?哪个更适合我?
- 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 接口的机械硬盘,统一设置休眠策略
于是,我们一起打磨出这个脚本:
✅ 核心逻辑
- 用 
lsblk -d -o NAME,ROTA,TRAN,TYPE扫描所有磁盘 - 筛选出:
TYPE=disk+ROTA=1(机械盘)+TRAN=usb - 对每个设备执行 
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
现在,我的小服务器安静地运行着,硬盘只在需要时醒来——这,就是极客的幸福。 😌