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

Bash脚本变量展开用于命令参数引发引号问题求助

解决rsync备份脚本的引号与参数传递问题

问题根源

  1. 参数存储方式错误:你用普通字符串RSYNC_OPTS存储rsync选项,当选项里包含带引号的规则(比如--filter='dir-merge,p- .gitignore'),最后执行时把整个字符串套进双引号,shell会把这一整串当成单个参数传给rsync,导致rsync误以为这是要同步的源路径,所以报错找不到目录。
  2. 目录存在性判断错误:if [[ ! -d "'${DIR}'" ]]这行里,你给目录路径加了额外的单引号,导致shell判断的是'/tmp/backup prep'(带单引号的路径),而实际目录是/tmp/backup prep,所以每次都认为目录不存在,重复执行mkdir。

修复后的脚本

#!/bin/bash
sudo -v
COMMAND=$(basename "$0")

SRC='/etc'
SRC=$(realpath "$SRC") # 转换为绝对路径
SRC="${SRC}/" # 强制rsync不允许指向父目录的符号链接

if [[ ! -d "$SRC" ]]; then
    echo "Use $COMMAND to backup directories only"
    exit 1
fi

BACKUP_DIR='/tmp/backup prep'
TMP_DIR="${BACKUP_DIR}/tmp"
DEST="${BACKUP_DIR}/$(basename "$SRC")"
LOG_DIR="$TMP_DIR"
LOG_FILE="${LOG_DIR}/${COMMAND}-$(date +%Y-%b-%d-%H-%M-%S-%N).log"

for DIR in "$BACKUP_DIR" "$TMP_DIR" "$LOG_DIR"; do
    if [[ ! -d "$DIR" ]]; then
        echo "Creating $DIR"
        sudo mkdir -p "$DIR"
    fi
done

# 用数组存储rsync选项,正确保留每个参数的边界
RSYNC_OPTS=(
    --dry-run
    --recursive
    --filter='dir-merge,p- .gitignore'
    --checksum
)

# 输出命令到日志
echo "sudo rsync ${RSYNC_OPTS[*]} '$SRC' '$DEST'" | sudo tee "$LOG_FILE"
echo --------
echo --------
echo
echo

set -x
# 展开数组,每个元素作为独立参数传递
sudo rsync "${RSYNC_OPTS[@]}" "$SRC" "$DEST"

关键改动说明

  • 改用数组存储选项:RSYNC_OPTS=(...)数组能正确区分每个rsync参数,不管参数里有没有空格或引号,展开时都会保持每个参数的独立性。
  • 修正目录判断:去掉[[ ! -d "'${DIR}'" ]]里的额外单引号,直接用[[ ! -d "$DIR" ]],正确判断实际目录是否存在。
  • 执行rsync时正确展开数组:"${RSYNC_OPTS[@]}"会把数组的每个元素作为单独的参数传给rsync,和你手动执行命令的效果完全一致。
  • 替换反引号为$():$(basename "$0")比反引号的可读性更好,嵌套时也不容易出错。
  • 添加mkdir -p:即使父目录不存在也能创建,同时避免重复创建已存在目录的错误(不过这里判断已经做了,加-p更稳妥)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 00:05:21