【发布时间】:2020-06-27 05:11:13
【问题描述】:
我有一个 Bash 脚本,我在其中调用 rsync 以执行到远程服务器的备份。要指定备份我的下载文件夹,我将"'${HOME}/Downloads'" 作为参数传递给rsync,它会产生输出:
rsync -avu '/Volumes/Norman Data/Downloads' me@example.com:backup/
使用上面扩展的变量(通过终端或脚本)运行命令可以正常工作,但是由于扩展变量中的空间以及引号(单个刻度)被包含在变量中时被忽略的事实作为参数的一部分传递(请参阅here),我能让它不阻塞空间的唯一方法是:
stmt="rsync -avu '${HOME}/Downloads' me@examle.com:backup/"
eval ${stmt}
在任何不是该脚本 100% 私有的东西上运行 eval 似乎会出现一些漏洞。我认为我应该以不同的方式做这件事是否正确?如果是这样,对于 bash-script-beginner 的任何提示将不胜感激。
** EDIT ** - 实际上我有更多的用例。上面的例子。对于传递的路径,我有一个数组,每个都包含空格,然后我将它们组合成 1 个类似的字符串
include_paths=(
"'${HOME}/dir_a'"
"'${HOME}/dir_b' --exclude=video"
)
for item in "${include_paths[@]}"
do
inc_args="${inc_args}" ${item}
done
inc_args 计算结果为 '/Volumes/Norman Data/me/dir_a' '/Volumes/Norman Data/me/dir_b' --exclude=video
然后我尝试将其作为参数传递给rsync,但单个刻度被读取为文字,并且由于空格而在第一个/Volumes/Norman 之后中断。
rsync -avu "${inc_args}" me@example.com:backup/
使用 eval 似乎将单个刻度读取为引号并执行:
rsync -avu '/Volumes/Norman Data/me/dir_a' '/Volumes/Norman Data/me/dir_b' --exclude=video me@example.com:backup/
就像我需要的那样。我似乎找不到其他工作方式。
** 编辑 2 - 解决方案 **
所以我需要做的第一件事就是将include_paths 数组修改为:
- 从双引号项中删除单个刻度
- 将任何特定于路径的标志(例如
--exclude)直接移动到它们应该应用到的路径之后的项目中
然后我构建了一个包含rsync 命令及其选项的数组,添加了扩展的include_paths 和exclude_paths 数组以及到远程主机的连接字符串。
最后扩展了该数组,它运行了我的整个正确引用的rsync 命令。最后修改后的数组include_paths是:
include_paths=(
"${HOME}/dir_a"
"${HOME}/dir_b"
"--exclude=video"
"${HOME}/dir_c"
)
我把所有东西放在一起:
cmd=(rsync -auvzP)
for item in "${exclude_paths[@]}"
do
cmd+=("--exclude=${item}")
done
for item in "${include_paths[@]}"
do
cmd+=("${item}")
done
cmd+=("me@example.com:backup/")
set -x
"${cmd[@]}"
【问题讨论】:
-
你可以像
$("$stmt")一样执行 -
@DigvijayS 这根本行不通。双引号告诉外壳程序将整个字符串——参数和所有——作为命令名,而不是作为命令名后跟参数。然后
$( )使shell 尝试执行任何能够作为第二个命令打印的内容。 -
是否有理由将命令放在变量中,而不是直接执行?如果是这样,取决于原因是什么,函数或数组将是一个更好的主意。见BashFAQ #50: I'm trying to put a command in a variable, but the complex cases always fail!
-
@GordonDavisson 有正确的想法。
eval仅在非常特殊的情况下才真正需要,例如eval "$(ssh-agent)"。 -
@Daveh0 将它们保存为数组形式,而不是转换为单个字符串。不要不要在数组元素中嵌入引号(引号围绕数据,而不是在数据中),并且不要将多个项目放在一个数组项(就像您对“dir_b”和“video”所做的那样)。您可以使用
"${array[@]/#/prefix}"成语为每个数组元素添加前缀。请参阅here 以获取非常相似情况的示例。
标签: bash security environment-variables eval