スクリプトでスペースを含む文字列を正しく置き換える方法

スクリプトでスペースを含む文字列を正しく置き換える方法

私のスクリプトには次のものがあります。

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: "/Users/me/some" の link_stat 失敗: 対応するファイルまたはディレクトリなし (2)

答え1

周囲に一重引用符を追加すると、$sshkey問題が解決します。

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

$sshkeyは二重引用符で囲まれた文字列内にあるため、呼び出し前にインタラクティブシェルによって拡張されますが、単一引用符は、接続を確立するために呼び出しが行われたrsyncときに文字列がもはや分割されるのを防ぎます。rsyncssh

$sshkeyそれ自体に一重引用符文字が含まれていないとします。

答え2

「内部」引用符を挿入する必要があります。私が言うこと

printf -v rsh_cmd 'ssh -i "%s" -d "%s"' "$sshkey" "$dstport"
...
rsync ... -e "$rsh_cmd"

答え3

-eこのオプションの引数は、シェルのコマンドrsyncラインではなくrsyncコマンドラインです。rsyncBourne シェルとは異なる独自のルールを使用して解析されます。

すべてのシェルと同様に、空白文字をパラメータ区切り文字として扱い、Bourneシェルと同様に、一重引用符と二重引用符(バックスラッシュを除く)を引用演算子として扱います。シェル拡張(たとえば$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"

これを行うために専用の関数を定義できます。再同期リファレンス良い:

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

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

別のオプションは、シェルを引数として渡してコマンドラインを解釈する-eことです。sshこれの1つの利点は、シェルが変数拡張を実行できることです。

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

関連情報