次の一連のコマンドがあります。
ssh -i key 10.10.10.10 mkdir -p "${DEST_PATH}/subdir1" "${DEST_PATH}/subdir2"
rsync "${SOURCE_PATH}" "$DEST_HOST:${DEST_PATH}/subdir1"
...
これらの供給変数は
DEST_PATH=$( myfun "foo" )
SOURCE_PATH=$( myfun "bar" )
これは順番に文字列を供給します。
function myfun {
local VALUE=$( grep "$1" yadayada.txt | tail -1 | sed 's/someregex//' )
echo "$VALUE"
}
$VALUE
スペースがあれば$VALUE
何をしますかHello World
?私はいつもそれを使用するとすべての""
問題が解決されると思いましたが、私の考えは間違っていました。他のいくつかの質問では、一重引用符を避けるか使用することを提案していますが、最良のアプローチが何であるかはよくわかりません。
ありがとう
答え1
xargs
リモートシステムに特定のオプションをサポートするコマンドがあることがわかっている場合は、-0
次のことができます。
printf '%s\0' "$DEST_PATH/subdir1" "$DEST_PATH/subdir2" |
ssh -i key 10.10.10.10 'xargs -0 mkdir -p --'
すべてのシェルはxargs -0 mkdir -p --
このシェルコードを同じように解釈します(sshd
リモートユーザーのログインシェルは、指定されたコードを解釈するためにリモートシステムで実行されていることに注意してくださいssh
)。
mkdir
リモートシェルのstdinを介して渡される引数のリストはNULで区切られますxargs
。
この方法のもう1つの利点は、必要な数のパラメータを渡すことができることです。必要にxargs
応じてリストが破損し、パラメータmkdir -p -- dirn dirn+1...
+環境リストのサイズ制限を回避するために複数の呼び出しが実行され、非常に大きなリストの完全なリストが送信される前にディレクトリの作成を開始することもできます。
バラよりリモートユーザーのログインシェルを知らず、SSHを介して任意の単純なコマンドをどのように実行できますか?他の方法の場合。
rsync
デフォルトでは、同期のためにリモートシェルコマンドラインの一部としてリモートファイル/ディレクトリ名を渡すため、それ自体同じ問題があります。より良い解決策は、シェルコードからファイル/ディレクトリ名を渡す代わりに-s
/を使用することです。--protect-args
、代わりにrsync
サーバーの標準入力に渡してください。
だから:
rsync -s -- "$SOURCE_PATH" "$DEST_HOST:$DEST_PATH/subdir1/"
マニュアルrsync
ページを引用すると、次のようになります。
スペースを含むファイル名を転送する必要がある場合、またはリモートシェルが理解するようにスペースをエスケープする必要がある場合は
--protect-args
()オプションを指定できます。-s
たとえば、rsync -av host:'file\ name\ with\ spaces' /dest
$ rsync --rsync-path='set -x; rsync' /etc/issue localhost:'1 2'
+zsh:1> rsync --server -e.LsfxC . 1 2
$ rsync --rsync-path='set -x; rsync' /etc/issue localhost:'1;uname>&2'
+zsh:1> rsync --server -e.LsfxC . 1
+zsh:1> uname
Linux
ファイル名がシェルコードとしてどのように解釈されるかを確認してください。
そして-s
:
~$ rsync -s --rsync-path='set -x; rsync' /etc/issue localhost:'1;uname>&2'
+zsh:1> rsync --server -se.LsfxC
リモートシェルコマンドラインにはファイル名のトレースはありません。
~$ cat '1;uname>&2'
Ubuntu 20.04.1 LTS \n \l
答え2
二重引用符を使用して解決する最もよくある質問しかし、これがすべての問題を解決するわけではありません。あなたの場合、二重引用符はローカルシェルからの不必要な拡張を防ぎますが、コマンドはssh
保護なしでファイル名をシェルフラグメントに直接挿入しますが、rsyncはコマンドラインからリモートファイル名を渡します。下のレイヤーは同じことを行います。つまり、そうであればDEST_PATH
コマンド/path/with three spaces
を実行しているのです。
mkdir -p /path/with three spaces/subdir1 /path/with three spaces/subdir2
リモートコンピュータから。二重引用符は、$DEST_PATH
スクリプトを実行するシェルが値を過度に分割しないようにしますが、リモートシェルではスペースが意味を持つことを保証します。
ネストされた引用符を素朴に使用すると、いいえこの問題を解決してください。たとえば、ssh -i key 10.10.10.10 mkdir -p "'${DEST_PATH}/subdir1'" "'${DEST_PATH}/subdir2'"
スペースを含むファイル名を使用できますが、スペースを含むファイル名は'
機能しません(攻撃者がの値を制御している場合、リモートコマンド実行の脆弱性である可能性がありますDEST_PATH
)。
最も簡単な方法は走ることですsshfsすべてをローカルパスとして扱います。ssh
あるいは、コマンドでファイル名を渡す必要がない場合は、rsync
ファイル名を引用することを心配する必要はありません。ただし、sshfsは常に管理が容易ではなく、rsyncを介して大容量ファイルを効率的に更新することはできません。
簡単なアプローチがうまくいかない場合は、引用された値が必要ですDEST_PATH
。つまり、またはのようなものに変更する必要があります/path/with three spaces
。'/path/with three spaces'
/path/with\ three\ \ spaces
set_quoted () {
raw="$1"
quoted=
while case "$raw" in *\'*) true;; *) false;; esac; do
quoted="$quoted'\\''${raw%%\'*}"
raw="${raw#*\'}"
done
quoted="'$quoted$raw'"
}
set_quoted "$DEST_PATH"; quoted_DEST_PATH="$quoted"
ssh -i key 10.10.10.10 mkdir -p "${quoted_DEST_PATH}/subdir1" "${quoted_DEST_PATH}/subdir2"
rsync "${SOURCE_PATH}" "$DEST_HOST:${quoted_DEST_PATH}/subdir1"
1 Rsync は、再帰またはローカルファイルの任意のファイル名に問題はありません。ただし、リモートシェルにコマンドライン引数をリモートファイル名として渡し、リモートシェルはそれを拡張します。
答え3
これは、引数をシェル入力として再利用できるGNUシステムがある場合にprintf
サポートされます。%q
だからあなたはできます
DEST_PATH_ESC="$(printf "%q" "$DEST_PATH")
ssh -i key 10.10.10.10 mkdir -p "${DEST_PATH_ESC}/subdir1" "${DEST_PATH_ESC}/subdir2"
rsync "${SOURCE_PATH}" "$DEST_HOST:${DEST_PATH_ESC}/subdir1" # not sure if you need it here
テストしてみましょう:
a="some complicated thing with spaces
and such"
b=$(printf "%q" "$a")
echo "$b"
私達は得た
$'some complicated thing with spaces\nand such'
したがって、これは空白だけでなく改行に対しても非常に安全でなければなりません。