O script de backup falha com as opções do comando rsync em uma variável entre aspas

0

Usando: versão do protocolo rsync versão 3.1.0 31; Linux Mint 17 (baseado no: Ubuntu 14.04.3)

Em um dos meus scripts Bash de backup, que usa rsync, eu coloco as opções de rsync em uma variável como esta:

# 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"

E tenho erros:

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]

Instintivamente, removi as aspas em torno de "$rsync_options" no script do Bash e tentei novamente ...

rsync $rsync_options --log-file="$log_file" --exclude-from="$exclude_file" \
      "$source_dir" "$destination_dir"

... isso consertou e funcionou perfeitamente.

Colocar as opções de um comando em variáveis em scripts Bash é algo que já fiz muitas vezes, mas lembro-me que de vez em quando eu tive que remover aspas duplas em torno do nome de uma variável, embora eu possa ' Não pense em nenhuma ocasião específica (além desta obviamente).

Minha pergunta é por que as citações causaram problemas e foram causadas por algo específico do rsync ou do Bash? Eu gostaria de entender isso.

Obrigado.

    
por mattst 10.02.2016 / 19:13

3 respostas

1

Quando você citou o rsync_options para o comando rsync, passou todas essas opções como um argumento para o rsync, em vez de como opções separadas. Assim, o rsync estava tentando executar o ssh com os sinalizadores "-axhPv".

Tempo de demonstração:

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
    
por 10.02.2016 / 20:02
4

O conjunto de opções pode ser visualizado usando printf :

$ printf '<%s>  '  "-e ssh -axhPv"  arg2  arg3 ; echo
<-e ssh -axhPv>  <arg2>  <arg3> 

Se as aspas forem removidas, isso é o que você recebe:

$ printf '<%s>  '  -e ssh -axhPv  arg2  arg3 ; echo
<-e>  <ssh>  <-axhPv>  <arg2>  <arg3> 

Ou seja: os argumentos são divididos em espaços e dados como elementos separados no comando.

Matriz

No entanto, a solução geral não é remover aspas. Isso expõe cada argumento à divisão do IFS e à expansão do nome de caminho.

A solução correta é usar matrizes de valores:

$ a=(-e ssh -axhPv  arg2  arg3)
$ printf '<%s>  ' "${a[@]}"; echo
<-e>  <ssh>  <-axhPv>  <arg2>  <arg3>   

Isso permite adicionar opções e até mesmo opções com espaços:

$ a+=("arg with spaces" "one more")
$ printf '<%s>  ' "${a[@]}"; echo
<-e>  <ssh>  <-axhPv>  <arg2>  <arg3>  <arg with spaces>  <one more>  

Então você mantém o controle sobre como os argumentos serão dados ao comando.

Seu script foi reescrito:

#!/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[@]}"

CAVEAT: Uma coisa que você não pode colocar dentro da matriz é redirecionar.

    
por 11.02.2016 / 03:39
0

Eu acho que o argumento para -e precisa estar entre aspas.

rsync -axhPv -e 'ssh -p 2222' user@host:/source_dir/ /dest_dir/

Então, você pode precisar fazer algo nos moldes de:

rsync_options="-e 'ssh' -axhPv"

Você pode ou não precisar escapar das aspas simples dentro das duplas.

    
por 10.02.2016 / 19:43