在我的脚本中我有:
sshkey="/Users/me/some path/with spaces/id_rsa"
dstport=...
dstparent=...
dstuserhost=...
rsync -az --delete -e "ssh -i $sshkey -p $dstport" $src $dstuserhost:$dstparent
当我运行它时,我得到:
rsync:link_stat“/Users/me/some”失败:没有这样的文件或目录(2)
答案1
只需在周围添加单引号$sshkey
即可解决该问题:
rsync -az --delete -e "ssh -i '$sshkey' -p '$dstport'" "$src" "$dstuserhost:$dstparent"
该$sshkey
值将在调用之前由交互式 shell 扩展,因为它位于双引号字符串内,但单引号会阻止在调用设置连接rsync
时进一步拆分字符串。rsync
ssh
假设$sshkey
本身不包含单引号字符。
答案2
您需要注入一些“内部”引号。我会说
printf -v rsh_cmd 'ssh -i "%s" -d "%s"' "$sshkey" "$dstport"
...
rsync ... -e "$rsh_cmd"
答案3
-e
该选项的参数rsync
不是 shell 命令行,而是rsync
命令行。rsync
使用与 Bourne shell 不同的自己的规则来解析它。
与所有 shell 一样,它将空格字符视为参数分隔符,并且与 Bourne shell 一样,它将单引号和双引号(但不包括反斜杠)视为引用运算符。它不执行任何 shell 扩展(如$var
、$(cmd)
、*.txt
、~user
...)。
因此,要在该伪命令行中嵌入任意参数,您可以将其括在双引号中,但双引号字符本身除外(然后将其括在单引号中),或者将它们括在单引号中,但单引号字符本身除外(然后用双引号括起来)。
例如,如果您的 ssh 密钥文件是/cygdrive/c/John Doe/.ssh/John "Dude" Doe's.rsa
,则 的参数-e
应该类似于'/cygdrive/c/John Doe/.ssh/John "Dude" Doe'"'"'s.rsa'
或"/cygdrive/c/John Doe/.ssh/John "'"'"Dude"'"'" Doe's.rsa"
。
您可以定义一个专用函数来执行此操作rsync 引用喜欢:
# ksh93/bash/zsh syntax:
rsync_quote() {
local arg="$1"
arg=${arg//\'/\'\"\'\"\'}
printf "'%s'\n" "$var"
}
rsync -e "ssh -i $(rsync_quote "$sshkey") -p $dstport" ...
另一种选择是将 shell 作为参数传递给 shell,-e
然后解释ssh
命令行。这样做的一个优点是 shell 可以进行变量扩展。
KEY=$sshkey PORT=$dstport SSH_COMMAND='ssh -i "$KEY" -p "$PORT" "$@"' \
rsync -e "sh -c 'eval \"\$SSH_COMMAND\"' sh" ...
如果您使用 运行它strace -fe execve -s 999
,您会看到它展开:
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