複数の SSH を介してリモート マシン上の引数に空白を含むローカル スクリプトを実行するにはどうすればよいでしょうか?

複数の SSH を介してリモート マシン上の引数に空白を含むローカル スクリプトを実行するにはどうすればよいでしょうか?

私の質問に似ているここ複数のsshを使用する点を除いて、この質問ただし、引数に空白が入っています...

test.sh複数の ssh を介してリモート サーバー上でローカル スクリプト を実行したいと思います。test.shは、多くの場合複数の単語を含む引数を取ります。

テスト:

#!/bin/bash
while getopts b: opt;
do
  case $opt in
    b)
       bval="$OPTARG"
       ;;
  esac
done
echo $bval

呼び出しテスト

#!/bin/bash
# Do some stuff
PHRASE="multi word arg"
ssh -A user@host1 "bash -s" -- < ./test.sh -b "${PHRASE@Q}"

を実行すると./call_test.sh、正しく出力されます。

multi word arg

しかし、最後の行をcall_test.sh次のように変更すると:

ssh -A user@host1 ssh -A user@host2 "bash -s" -- < ./test.sh -b "${PHRASE@Q}"

を実行すると./call_test.sh、次のように出力されます。

multi

基本的に、単一の SSH から複数の SSH に変更すると、私の複数語の議論は崩れてしまいます。

複数の ssh コマンドが各ステップで複数語の引数の引用符をアンラップしていると思いますが、それを防ぐ方法がわかりません。複数の ssh で実行されているスクリプトに複数語の引数を正常に渡す方法はありますか?

編集:

私は信じているこの答え私が直面している問題に迫ります。

答え1

2 つの ssh コマンドに渡す場合は、文字列を 2 回引用符でエスケープする必要があります。追加の割り当てを使用してこれを実行しますPHRASE=${PHRASE@Q}

ただし、および(以下で説明)は引用符エスケープ形式を使用するため、リモートシェルではサポートされない可能性があることに注意し${var@Q}printf %qください$'...'

test.shスクリプトの最後の行をecho "$bval"ではなく に修正した後echo $bval:

PHRASE="multi word     arg"
PHRASE=${PHRASE@Q}
ssh -A user@host1 ssh -A user@host2 "bash -s" -- < ./test.sh -b "${PHRASE@Q}"

multi word     arg

${var@P}bashの古いバージョンではサポートされていない拡張形式の代わりに、以下を使用できます。printf -v var %q:

PHRASE="multi word     arg"
printf -v PHRASE %q "$PHRASE"
printf -v PHRASE %q "$PHRASE"
ssh -A user@host1 ssh -A user@host2 "bash -s" -- < ./test.sh -b "$PHRASE"

また、非効率的な stdin 経由でスクリプトを渡す代わりに (bash は stdin からスクリプトを読み込むときにバイトごとに読み取りシステム コールを実行します)、スクリプト全体とその引数を変数に格納することもできます。

printf -v cmd 'bash -c %q bash -b %q' "$(cat test.sh)" 'multi word   wahterve'
printf -v cmd %q "$cmd"
ssh localhost ssh localhost "$cmd"

multi word   wahterve

関連情報