1. 引数内のスクリプト

1. 引数内のスクリプト

マシンA経由でマシンBに接続したいとします。私が学んだように、これを実現するには複数の方法があります。たとえば、

ssh -T -J user@machineA user@machineB << END_OF_SSH_CONNECTION
  # Some commands in machine B
END_OF_SSH_CONNECTION

または

ssh -T user@machineA << END_OF_MACHINE_A
  ssh -T machine B << END_OF_MACHINE_B
    # Some commands in machine B
  END_OF_MACHINE_B
END_OF_MACHINE_A

しかし、マシン B に接続したときにユーザーがコマンドを入力できる対話型シェルを起動する方法がわかりません。たとえば、次のようにします。

ssh user@machineA -t 'bash -l -c "bash"'

そしてこれも同様に機能します:

ssh -J user@machineA user@machineB -t 'bash -l -c "bash"'

ただし、次の方法は機能しません。

ssh -J user@machineA user@machineB -t 'sudo apt-get update; bash -l -c "bash"'
E: Command line option 'l' [from -l] is not understood in combination with the other options.
Connection to machineB closed.

また、以下のいずれでもありません。

ssh -J user@machineA user@machineB -t 'if [ 1 -eq 1 ]; then bash -l -c "bash"; fi'
/usr/local/sbin/start_ttylog.sh: line 67: exec: if: not found
Connection to machineB closed.

これも試してみました:

ssh -T -J user@machineA user@machineB << EOF

  # Some commands on machineB

  # Check whether apache2 has been correctly installed
  if [ $(dpkg-query -W -f='${Status}' apache2 2>/dev/null | grep -c "ok installed") -eq 1 ];
  then
    printf "done\n";
  else
    printf "fail\nStarting an interactive shell: enter exit to quit\n";
    bash -l -c "bash";
  fi
EOF

この最後のバージョンはほぼ動作します (つまり、エラーは返されません) が、ユーザーが期待どおりにコマンドを入力できる対話型シェルは起動しません。

誰かが私を助けてくれることを願っています。よろしくお願いします。

答え1

助けてくれてありがとう、@attie。提案されたコードを少し修正したので、以下に報告します。

#!/usr/bin/env bash

ssh -T -J user@machineA user@machineB 'cat >/tmp/myscript' <<"EOF"
if dpkg-query -W -f='${Status}' apache2 2>/dev/null | grep -q 'ok installed'; then
  echo "done";
else
  echo "fail, starting an interactive shell";
  bash -l -c "bash";
fi
EOF

ssh -t -J user@machineA user@machineB "bash '/tmp/myscript'"

これに外部ファイルに書き込む必要さえありません。

#!/usr/bin/env bash

ssh -T -J user@machineA user@machineB << "EOF"
if dpkg-query -W -f='${Status}' apache2 2>/dev/null | grep -q "ok installed"; then
  echo "done";
else
  echo "fail, starting an interactive shell";
  bash -l -c "bash";
fi
EOF

EOF私は基本的に、フィールドを拡張する試みを避けるために使用する の周りの引用符を忘れていました${Status}

答え2

リモートホストでスクリプトを実行したい場合そして対話型シェルを取得する場合、次の 2 つのオプションがあります。

  1. スクリプト全体を のコマンドライン引数に入力しますssh
  2. スクリプトをリモートホストにプッシュし(例:scpまたはを使用ssh)、それを実行します(例: を使用ssh

1. 引数内のスクリプト

最後の例は「長さこのアプローチでは「」を使用しますが、読みやすさを向上させるためにmapfile( の組み込み関数bash)。"EOF"を展開しないように引用符で囲んでいることに注意してください${Status}

mapfile -t SCRIPT <<"EOF"
if dpkg-query -W -f='${Status}' apache2 2>/dev/null | grep -q 'ok installed'; then
  echo "done";
else
  echo "fail, starting an interactive shell";
  bash -l -c "bash";
fi
EOF

ssh -t -J user@machineA user@machineB "${SCRIPT[@]}"

2. スクリプトをプッシュして実行する

使用後、クリーンアップするとボーナスポイントが付きますmktemp。注記を"EOF"再度引用します。

提供されたエラー メッセージの 1 つ ( の周辺exec、詳細は下記を参照) に基づくと、 のこの使用法はcat >/tmp/myscriptおそらく機能しません。

ssh -T -J user@machineA user@machineB 'cat >/tmp/myscript' <<"EOF"
if dpkg-query -W -f='${Status}' apache2 2>/dev/null | grep -q 'ok installed'; then
  echo "done";
else
  echo "fail, starting an interactive shell";
  bash -l -c "bash";
fi
EOF

ssh -t -J user@machineA user@machineB "bash '/tmp/myscript'"

ステップ 1 では、常に問題を最も単純な形に分解してみる必要があります... 最後の呪文として、代わりに次のことを試してください。

ssh -T user@machineA << EOF
  bash -l -c "bash";
EOF

何が起こるでしょうか? リモート ホストからウェルカム メッセージが表示されますが、その後、セッションはエラーなしですぐに終了します。

変化-T(明示的に無効にする(PTY 割り当ての-t強制) の (PTY 割り当て) は、正しい方向へのヒントを提供します。

ssh -t user@machineA << EOF
  bash -l -c "bash";
EOF
Pseudo-terminal will not be allocated because stdin is not a terminal.

stdinこれは、スクリプトを提供するために を使用し、同じスクリプトstdin(ターミナルではなくパイプ) をリモート PTY に接続しようとしていることを示しています。

そこで、次の要素を削除してみましょうstdin

ssh -t user@machineA bash -l -c "bash"

そしてそれは機能します!

基本的にあなたはできないスクリプトとターミナルの両方で stdin を共有します。


その他のエラーについては:

E: Command line option 'l' [from -l] is not understood in combination with the other options.

もう一度、問題を見つけるために分解してください。残念ながら、これらのコマンドは私には機能しました。私がアクセスできるシステムが を使用しているかsh -c "${ARGS}"、または類似しているためだと思います。ただし、提供された別のエラーがヒントを与えてくれます:

/usr/local/sbin/start_ttylog.sh: line 67: exec: if: not found

execシステムが ...への引数として指定されたコマンドを実行するためにを使用しているようです。つまり、シェルはや のようなものを期待どおりにssh処理できないため、パス上にバイナリがないため失敗します。if;if

同様に、すべての引数がsudo一度に与えられます。

  1. sudo
  2. apt-get
  3. update;
  4. bash
  5. -l
  6. -c
  7. "bash"

確かに、sudoの(list) 引数は(login class)-lと相互に排他的です。-c

関連情報