备份脚本失败,rsync 命令的选项位于带引号的变量中

备份脚本失败,rsync 命令的选项位于带引号的变量中

使用: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"

您可能需要也可能不需要转义双打内的单引号。

相关内容