我正在从源数组列表中备份内容,有点像这个简化版本:
sources=( file1 file2 "other stuff" )
backupcommand="rsync -ar ${a[@]} dest/"
su backupuser -c "$backupcommand"
问题是$backupcommand
用正确的转义创建的。上面的意思是 rsync 查找一个名为“other”的文件和另一个名为“stuff”的文件,而不是一个名为“other stuff”的文件
我怎样才能构造一个引用的命令sh -c ...
(或ssh ...
等等)。我正在使用 bash。
答案1
首先,我们需要一种方法来显示 bash 数组中的内容sources
。一种好方法似乎是使用printf '%s\n' <args>
它将在 中每个参数输出一行<args>
。
例如:
$ sources=( file1 file2 "other stuff" )
$ printf '%s' "${sources[@]}"
file1
file2
other stuff
如果我们使用此命令作为我们的命令,backupcommand
我们可以看到备份程序(例如rsync
)实际上将接收什么作为其参数。因此,假设我们要通过bash -c ...
(或ssh ...
) 运行的命令是printf '%s\n' <args>
,并且我们有两个参数file1
和other stuff
。
我们要在目标 shell 上运行此命令:
$ printf '%s\n' file1 other\ stuff
file1
other stuff
因此,为了将其作为一个论点,我们需要引用它。我们可以使用""
or''
或\
来引用。我们可以printf '%s\n'
再次使用它,以检查我们是否已成功完成此操作......
$ # With backslashes:
$ printf '%s\n' printf\ \'%s\\n\'\ file1\ other\\\ stuff
printf '%s\n' file1 other\ stuff
$ # With single quotes
$ printf '%s\n' 'printf '\''%s\n'\'' file1 other\ stuff'
printf '%s\n' file1 other\ stuff
$ # Using a variable to store the command...
$ the_command='printf '\''%s\n'\'' file1 other\ stuff'
$ # ..then using that variable as a single argument...
$ printf '%s\n' "$the_command"
printf '%s\n' file1 other\ stuff
所以到目前为止一切都很好。在上一个版本中,我们引用了包含命令的变量。
现在我们可以使用数组作为参数吗?我们需要做的是引用/转义数组的每个项目并将其附加到单个字符串the_command
。
${var[@]}
告诉 bash 为每个数组项生成一个标记。但这些并没有被转义,因为它们已经被解释了。所以我们不能只是将其放入 shell 命令字符串中。所以我们需要一种应用引用的方法。
Bash 必须printf '%q' arg
引用arg
.我们可以使用命令替换$(...)
$ sources=( file1 other\ stuff )
$ the_command=printf\ \'%s\\n\'
$ for item in "${sources[@]}"
do the_command="$the_command "$(printf '%q' "$item")
done
$ printf '%s\n' "$the_command"
printf '%s\n' file1 other\ stuff
万岁!但是,我们可以像这样清理它:
$ sources=( file1 other\ stuff )
$ the_command="printf '%s\\n'"$(printf ' %q' "${sources[@]}")
$ printf '%s\n' "$the_command"
printf '%s\n' file1 other\ stuff
$ # And as final proof...
$ bash -c "$the_command"
file1
other stuff
应用于原来的问题:
sources=( file1 file2 "other stuff" )
backupcommand="rsync -ar "$(printf ' %q' "${sources[@]}")" dest/"
su backupuser -c "$backupcommand"
概括
printf '%q' arg
引号arg
- 命令替换
$(...)
根据命令的输出创建单个标记。 - 可以使用各种引用/转义方法;选择最可读的一个!
答案2
尝试在数组周围添加转义引号,如下所示
backupcommand="rsync -ar \"${a[@]}\" dest/"