Como substituir corretamente uma string contendo espaços no meu script

0

No meu script eu tenho:

sshkey="/Users/me/some path/with spaces/id_rsa"
dstport=...
dstparent=...
dstuserhost=...

rsync -az --delete -e "ssh -i $sshkey -p $dstport" $src $dstuserhost:$dstparent

E quando eu corro, recebo:

rsync: link_stat "/Users/me/some" failed: No such file or directory (2)

    
por Desmond Hume 26.11.2018 / 18:37

3 respostas

1

Basta adicionar aspas simples em torno de $sshkey para resolvê-lo:

rsync -az --delete -e "ssh -i '$sshkey' -p '$dstport'" "$src" "$dstuserhost:$dstparent"

O valor $sshkey seria expandido pelo seu shell interativo antes de chamar rsync porque ele está em uma cadeia com aspas duplas, mas as aspas simples impediriam que a sequência fosse dividida quando rsync chama ssh para definir a conexão.

Isso pressupõe que $sshkey não contenha caracteres de aspas simples em si.

    
por 26.11.2018 / 19:11
1

Você precisa injetar algumas cotações "internas". Eu diria

printf -v rsh_cmd 'ssh -i "%s" -d "%s"' "$sshkey" "$dstport"
...
rsync ... -e "$rsh_cmd"
    
por 26.11.2018 / 18:47
0

O argumento para a opção -e de rsync não é uma linha de comando do shell, é uma linha de comando rsync . rsync analisa as suas próprias regras que diferem das de Bourne.

Como todos os shells, ele trata os caracteres de espaço como separadores de argumentos e, assim como os shells Bourne, trata as aspas simples e aspas duplas (mas não as barras invertidas) como operadores de cotação. Ele não faz nenhuma expansão de shell (como $var , $(cmd) , *.txt , ~user ...).

Portanto, para incorporar um argumento arbitrário nessa pseudo linha de comando, você poderia incluí-lo entre aspas duplas, exceto o caractere de aspas duplas (que você incluiria entre aspas simples) ou incluí-las entre aspas simples, exceto para o único citar o próprio caractere (que você incluiria entre aspas duplas).

Por exemplo, se seu arquivo-chave ssh for /cygdrive/c/John Doe/.ssh/John "Dude" Doe's.rsa , o argumento para -e deve ser algo como '/cygdrive/c/John Doe/.ssh/John "Dude" Doe'"'"'s.rsa' ou "/cygdrive/c/John Doe/.ssh/John "'"'"Dude"'"'" Doe's.rsa" .

Você pode definir uma função dedicada para fazer isso rsync-quoting como:

# ksh93/bash/zsh syntax:
rsync_quote() {
  local arg="$1"
  arg=${arg//\'/\'\"\'\"\'}
  printf "'%s'\n" "$var"
}

rsync -e "ssh -i $(rsync_quote "$sshkey") -p $dstport" ...

Outra opção seria passar um shell como argumento para -e , que seria o interpretador da linha de comando ssh . Uma vantagem disso é que o shell poderia fazer as expansões variáveis.

KEY=$sshkey PORT=$dstport SSH_COMMAND='ssh -i "$KEY" -p "$PORT" "$@"' \
  rsync -e "sh -c 'eval \"\$SSH_COMMAND\"' sh" ...

Se você executá-lo com strace -fe execve -s 999 , será exibido:

execve("/usr/bin/rsync", ["rsync", "-e", "sh -c 'eval \"$SSH_COMMAND\"' sh", "1/", "localhost:2/"], 0x7ffc03b83678 /* 74 vars */) = 0
strace: Process 7208 attached
[pid  7208] execve("/bin/sh", ["sh", "-c", "eval \"$SSH_COMMAND\"", "sh", "localhost", "rsync", "--server", "-e.LsfxC", ".", "2/"], 0x7ffcc6ad0f28 /* 74 vars */) = 0
[pid  7209] execve("/usr/bin/ssh", ["ssh", "-i", "/cygdrive/c/John Doe/.ssh/John \"Dude\" Doe's.rsa", "-p", "2222", "localhost", "rsync", "--server", "-e.LsfxC", ".", "2/"], 0x5651a0c144a8 /* 74 vars */) = 0
    
por 27.11.2018 / 08:28