我正在尝试运行rsync命令,将文件复制到新位置。如果我直接运行rsync命令,在命令行上没有任何参数扩展,rsync就能达到我的期望
$ rsync -amnv --include='lib/***' --include='arm-none-eabi/include/***' \
--include='arm-none-eabi/lib/***' --include='*/' --exclude='*' \
/tmp/from/ /tmp/to/
building file list ... done
created directory /tmp/to
./
arm-none-eabi/
arm-none-eabi/include/
arm-none-eabi/include/_ansi.h
...
arm-none-eabi/lib/
arm-none-eabi/lib/aprofile-validation.specs
arm-none-eabi/lib/aprofile-ve.specs
...
lib/
lib/gcc/
lib/gcc/arm-none-eabi/
lib/gcc/arm-none-eabi/4.9.2/
lib/gcc/arm-none-eabi/4.9.2/crtbegin.o
...
sent 49421 bytes received 6363 bytes 10142.55 bytes/sec
total size is 423195472 speedup is 7586.32 (DRY RUN)
但是,如果我将过滤器参数括在一个变量中,并使用该变量调用该命令,则会观察到不同的结果。 rsync
复制了我 not 期望的许多额外目录:
$ FILTER="--include='lib/***' --include='arm-none-eabi/include/***' \
--include='arm-none-eabi/lib/***' --include='*/' --exclude='*'"
$ rsync -amnv ${FILTER} /tmp/from/ /tmp/to/
building file list ... done
created directory /tmp/to
./
arm-none-eabi/
arm-none-eabi/bin/
arm-none-eabi/bin/ar
...
arm-none-eabi/include/
arm-none-eabi/include/_ansi.h
arm-none-eabi/include/_syslist.h
...
arm-none-eabi/lib/
arm-none-eabi/lib/aprofile-validation.specs
arm-none-eabi/lib/aprofile-ve.specs
...
bin/
bin/arm-none-eabi-addr2line
bin/arm-none-eabi-ar
...
lib/
lib/gcc/
lib/gcc/arm-none-eabi/
lib/gcc/arm-none-eabi/4.9.2/
lib/gcc/arm-none-eabi/4.9.2/crtbegin.o
...
sent 52471 bytes received 6843 bytes 16946.86 bytes/sec
total size is 832859156 speedup is 14041.53 (DRY RUN)
如果我echo
命令失败,它会生成成功的确切命令。复制输出并直接运行可以得到预期的结果。
对于bash参数扩展的工作方式,我显然缺少一些东西。有人可以解释为什么两个不同的调用会产生不同的结果吗?
答案 0 :(得分:6)
shell在扩展变量之前解析引号,因此将引号放在变量的值中并不能达到预期的效果 - 当它们到位时,它也是如此迟到他们做任何有用的事情。有关详细信息,请参阅BashFAQ #50: I'm trying to put a command in a variable, but the complex cases always fail!。
在您的情况下,看起来解决此问题的最简单方法是使用数组而不是纯文本变量。这样,在创建数组时会解析引号,每个" word"作为单独的数组元素存储,如果正确引用变量(使用双引号和[@]
),数组元素将包含在命令的参数列表中,而不会进行任何不需要的解析:
filter=(--include='lib/***' --include='arm-none-eabi/include/***' \
--include='arm-none-eabi/lib/***' --include='*/' --exclude='*')
rsync -amnv "${filter[@]}" /tmp/from/ /tmp/to/
请注意,数组在bash和zsh中可用,但不是所有其他与POSIX兼容的shell。另外,我降低了filter
变量名称 - 推荐的做法,以避免与shell的特殊变量(全部为大写)发生冲突。
答案 1 :(得分:1)
为了方便起见,我喜欢将参数分成不同的行:
ROPTIONS=(
-aNHXxEh
--delete
--fileflags
--exclude-from=$EXCLUDELIST
--delete-excluded
--force-change
--stats
--protect-args
)
然后这样称呼它:
rsync "${ROPTIONS[@]}" "$SOURCE" "$DESTINATION"