You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何我的rsync备份函数中会出现多余单引号?

问题描述

我正在为多设备创建一个使用rsync执行全系统备份的函数,为排查问题启用了set -x来查看命令实际执行情况,发现--exclude={...}部分被多余的单引号包裹,导致功能无法正常运行。

函数代码

function system-backup {
    u_excl=("/dev/*", "/proc/*", "/sys/*", "/tmp/*", "/run/*", "/mnt/*", "/media/*", "/lost+found")
    source=$HOSTNAME
    sourcedir='/'
    dest='192.168.1.51:'

    # determine additional folders to exclude and other options
    if [ $HOSTNAME == my-pc ]; then
            u_excl+=(,"/home/*")
    fi

    excl=$(printf {"%s" "${u_excl[@]}")

    ( set -x; sudo rsync -aAXHv --delete --exclude={"$excl"} "$sourcedir" "$dest"/mnt/external/bkps/"$source"_bkp )
}
export -f system-backup

执行输出

$ system-backup
+ sudo rsync -aAXHv '--exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found","/home/*"}' / 192.168.1.51:/mnt/external/bkps/my-pc_bkp

问题原因
  1. 数组定义错误:bash数组元素之间不需要逗号,你在定义u_excl和追加元素时加的逗号,会被当成元素内容的一部分保留下来,后续拼接时直接带入了最终字符串。
  2. 变量展开时机错误:你把数组元素拼接成excl变量后,再放到--exclude={"$excl"}中,bash会将整个{"$excl"}解析为一个单一字符串参数,用单引号包裹标记这是一个整体,导致rsync无法识别其中的多个排除路径——rsync需要的是多个独立的排除规则,而非一个包含所有规则的字符串。

修复方案

方案1:为每个排除项单独添加--exclude参数(最可靠)

直接构建rsync的参数数组,每个排除项对应一个--exclude参数:

function system-backup {
    # 数组定义去掉多余逗号
    u_excl=( "/dev/*" "/proc/*" "/sys/*" "/tmp/*" "/run/*" "/mnt/*" "/media/*" "/lost+found" )
    source=$HOSTNAME
    sourcedir='/'
    dest='192.168.1.51:'

    if [ "$HOSTNAME" == "my-pc" ]; then
            # 追加元素同样去掉逗号
            u_excl+=( "/home/*" )
    fi

    # 构建rsync排除参数列表
    rsync_excl=()
    for path in "${u_excl[@]}"; do
        rsync_excl+=( "--exclude=$path" )
    done

    ( set -x; sudo rsync -aAXHv --delete "${rsync_excl[@]}" "$sourcedir" "$dest"/mnt/external/bkps/"$source"_bkp )
}
export -f system-backup

方案2:正确利用bash大括号展开

如果坚持用大括号格式,需要让bash在数组展开时直接完成拼接,避免先存为变量再展开:

function system-backup {
    u_excl=( "/dev/*" "/proc/*" "/sys/*" "/tmp/*" "/run/*" "/mnt/*" "/media/*" "/lost+found" )
    source=$HOSTNAME
    sourcedir='/'
    dest='192.168.1.51:'

    if [ "$HOSTNAME" == "my-pc" ]; then
            u_excl+=( "/home/*" )
    fi

    # 生成逗号分隔的排除路径字符串,去掉末尾多余逗号
    excl_str=$(printf "%s," "${u_excl[@]}")
    excl_str=${excl_str%,}

    # 不包裹引号,让bash解析大括号展开
    ( set -x; sudo rsync -aAXHv --delete --exclude={$excl_str} "$sourcedir" "$dest"/mnt/external/bkps/"$source"_bkp )
}
export -f system-backup

额外注意事项

  • bash数组元素间不需要逗号,添加逗号会导致元素包含无效字符。
  • set -x的输出中用单引号包裹参数是bash的标记方式,本身不是问题,但如果参数被错误合并成一个字符串,就会导致rsync无法处理。
  • 比较变量时建议给两边都加双引号,避免空值或特殊字符引发逻辑错误。

内容的提问来源于stack exchange,提问作者meijin3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 17:57:14