为何我的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
问题原因
- 数组定义错误:bash数组元素之间不需要逗号,你在定义
u_excl和追加元素时加的逗号,会被当成元素内容的一部分保留下来,后续拼接时直接带入了最终字符串。 - 变量展开时机错误:你把数组元素拼接成
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
相关产品推荐
相关产品推荐

