有人能解释一下我最近通过 rsync 将目录同步到另一台服务器时注意到的这种行为吗?在这种情况下,我想排除子目录“done”。
当我将要与 rsync 一起使用的选项放入变量中时,该目录未被排除。但是当我将选项直接放在 rsync 调用后面时,该目录被排除。对“-av”的更改有所不同,但排除不起作用。
rsync 3.0.9-18,在 bash 上,CentOS 7.4
不排除:
$ RSYNC_OPTIONS='-av --exclude "done"'
$ touch done/test.ignore && rsync ${RSYNC_OPTIONS} ${SOURCEDIR} ${TARGET}
sending incremental file list
done/test.ignore
sent 132 bytes received 32 bytes 109.33 bytes/sec
total size is 0 speedup is 0.00
不包括:
$ touch done/test.ignore && rsync -av --exclude "done" ${SOURCEDIR} ${TARGET}
sending incremental file list
sent 41 bytes received 12 bytes 106.00 bytes/sec
total size is 0 speedup is 0.00
答案1
我复制了这个问题,并查看了set -x
这两个命令的真实情况。结果发现这个命令
rsync ${RSYNC_OPTIONS} ${SOURCEDIR} ${TARGET}
实际上相当于
rsync -av --exclude '"done"' ${SOURCEDIR} ${TARGET}
注意引号中的引号。您的模式不是done
;而是"done"
,就好像您要排除的目录在其实际名称中有双引号一样。
为了几乎修复这个问题,你可以在声明变量时不用这些麻烦的引号:
RSYNC_OPTIONS='-av --exclude done' # poor fix, don't
rsync ${RSYNC_OPTIONS} "${SOURCEDIR}" "${TARGET}"
但如果模式包含空格等,这将适得其反。另一种方法可能是eval
:
RSYNC_OPTIONS='-av --exclude "name with double spaces"'
eval rsync "${RSYNC_OPTIONS}" '"${SOURCEDIR}" "${TARGET}"' # not recommended
eval
将第二次解析该行。正确安全地使用它非常困难。我使用双引号${RSYNC_OPTIONS}
,因此"name with double spaces"
不会丢失双空格。我使用单引号"${SOURCEDIR}" "${TARGET}"
,因此这些变量不会立即扩展(否则它们的内容将进行扩展)。这很棘手!
此外,怎么样name "with' quotes
?要将这个精确的字符串作为选项参数,rsync --exclude
您需要在声明中使用一些模糊的引用和转义RSYNC_OPTIONS
。还有更多理由避免eval
。
真正的解决方案是在 Bash 中使用数组。请注意,数组不可移植。
RSYNC_OPTIONS=(-av --exclude 'name with spaces and $u(h')
rsync "${RSYNC_OPTIONS[@]}" "${SOURCEDIR}" "${TARGET}"
我理解为什么你没有${RSYNC_OPTIONS}
在原来的方法中使用引号。但你应该${SOURCEDIR}
分别引用和${TARGET}
。上面的命令正确地引用了每个变量。
或者也许是${SOURCEDIR}
为了指定多个来源?这可能是不引用它的原因,但它可能会带来与类似的问题${RSYNC_OPTIONS}
。在这种情况下,您也应该在这里使用数组变量。
还请考虑变量名小写。
答案2
那么像这样放置引文怎么样?
RSYNC_OPTIONS='-av --exclude '"done"''
我仅用简化的命令进行了测试:
SEP='-F ";"'
echo "1;2;3" | awk ${SEP} '{print $2}'
(不工作)
SEP='-F '";"''
echo "1;2;3" | awk ${SEP} '{print $2}'
(在职的)
编辑:
使用@Kamil 技术,数组也能很好地工作:
SEP=(-F ";")
echo "1;2;3" | awk ${SEP[@]} '{print $2}'