ディレクトリにスペースを許可するには、このコードスニペットをどのように適切に処理する必要がありますか?

ディレクトリにスペースを許可するには、このコードスニペットをどのように適切に処理する必要がありますか?

次の一連のコマンドがあります。

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'

したがって、これは空白だけでなく改行に対しても非常に安全でなければなりません。

関連情報