使用:rsync版本3.1.0协议版本31; Linux Mint 17(基于:Ubuntu 14.04.3)
在我的一个使用 rsync 的备份 Bash 脚本中,我将 rsync 选项放入一个变量中,如下所示:
# Set rsync command options.
rsync_options="-e ssh -axhPv"
if [ "$deletion_type" = "DELETE_ON_DESTINATION" ]; then
rsync_options="$rsync_options --delete"
fi
if [ "$run_type" = "DRY_RUN" ]; then
rsync_options="$rsync_options --dry-run"
fi
# Run the backup.
rsync "$rsync_options" --log-file="$log_file" --exclude-from="$exclude_file" \
"$source_dir" "$destination_dir"
并出现错误:
unknown option -- h
usage: ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
[-D [bind_address:]port] [-E log_file] [-e escape_char]
[-F configfile] [-I pkcs11] [-i identity_file]
[-L [bind_address:]port:host:hostport] [-l login_name] [-m mac_spec]
[-O ctl_cmd] [-o option] [-p port]
[-Q cipher | cipher-auth | mac | kex | key]
[-R [bind_address:]port:host:hostport] [-S ctl_path] [-W host:port]
[-w local_tun[:remote_tun]] [user@]hostname [command]
rsync: connection unexpectedly closed (0 bytes received so far) [sender]
rsync error: unexplained error (code 255) at io.c(226) [sender=3.1.0]
"$rsync_options"
我本能地删除了Bash 脚本中的引号并再次尝试......
rsync $rsync_options --log-file="$log_file" --exclude-from="$exclude_file" \
"$source_dir" "$destination_dir"
...这解决了它,而且效果很好。
在 Bash 脚本中将命令的选项放在变量中是我做过很多次的事情,但我确实记得有时我必须删除变量名称周围的双引号,尽管我想不出任何特定场合(显然除了这个)。
我的问题是为什么引号会引起问题,它们是由 rsync 或 Bash 特有的东西引起的吗?我想了解一下这个问题。
谢谢。
答案1
可以使用以下方式可视化选项集printf
:
$ printf '<%s> ' "-e ssh -axhPv" arg2 arg3 ; echo
<-e ssh -axhPv> <arg2> <arg3>
如果删除引号,您将得到以下结果:
$ printf '<%s> ' -e ssh -axhPv arg2 arg3 ; echo
<-e> <ssh> <-axhPv> <arg2> <arg3>
也就是说:参数按空格分割,并作为单独的元素提供给命令。
大批
然而,一般的解决方案是不删除引号。这将每个参数暴露给 IFS 分割和路径名扩展。
正确的解决方案是使用值数组:
$ a=(-e ssh -axhPv arg2 arg3)
$ printf '<%s> ' "${a[@]}"; echo
<-e> <ssh> <-axhPv> <arg2> <arg3>
这使得可以添加选项,甚至是带有空格的选项:
$ a+=("arg with spaces" "one more")
$ printf '<%s> ' "${a[@]}"; echo
<-e> <ssh> <-axhPv> <arg2> <arg3> <arg with spaces> <one more>
因此,您保留对如何向命令提供参数的控制权。
你的脚本重写:
#!/bin/bash
# Set rsync command options.
rsync_options=( "-e" "ssh" "-axhPv" )
if [ "$deletion_type" = "DELETE_ON_DESTINATION" ]; then
rsync_options+=( "--delete" )
fi
if [ "$run_type" = "DRY_RUN" ]; then
rsync_options+=("--dry-run")
fi
log_file="/var/tmp/log/script.sh"
exclude_file="/var/tmp/log/excludethis"
source_dir=/a
destination_dir=/b
# Run the backup.
rsync_options+=('--log-file="'"$log_file"'"')
rsync_options+=('--exclude-from="'"$exclude_file"'"')
rsync_options+=("$source_dir")
rsync_options+=("$destination_dir")
### Remove the printf to actually run the command:
printf '<%s> ' rsync "${rsync_options[@]}"
注意:不能放入数组中的一件事是重定向。
答案2
当您将 rsync_options 引用到 rsync 命令时,您将所有这些选项作为一个参数传递给 rsync,而不是作为单独的选项。因此,rsync 尝试使用“-axhPv”标志运行 ssh。
演示时间:
function showme () {
echo First: $1
echo Second: $2
echo Third: $3
}
$ showme "-e ssh -axhPv" two three
First: -e ssh -axhPv
Second: two
Third: three
$ showme -e ssh -axhPv two three
First: -e
Second: ssh
Third: -axhPv
答案3
我认为 -e 的参数本身需要用引号引起来。
rsync -axhPv -e 'ssh -p 2222' user@host:/source_dir/ /dest_dir/
因此,您可能需要执行以下操作:
rsync_options="-e 'ssh' -axhPv"
您可能需要也可能不需要转义双打内的单引号。