if elseに含まれる複数のコマンドをsshで実行して実行する方法は?

if elseに含まれる複数のコマンドをsshで実行して実行する方法は?

SSHを使用してさまざまなサーバーからいくつかのデータをキャプチャし、特定の条件でいくつかのコマンドを実行したいと思います。

私はしたくありません:

if ssh $host test -f /file; then
  # If file exists
  var=$(ssh $host long pipeline)
else
  # if it doesn't
  var=$(ssh $host another long pipeline)
fi

なぜなら、プロセスが長くなるからです。リモートコンピュータでif elseを実行したいです。


いくつかの方法を試しましたが、成功しませんでした。

var=$(ssh $host if [ -f /file ]\; then long pipeline1 \; else long pipeline2 \; fi)

ベース回答、動作しますが、パイプライン1の最後のコマンドは、そのコマンドとelseパイプライン2の残りの部分を引数と見なします。

command: can't read else: No such file or directory
...
command: can't read fi: No such file or directory

それからこれを試しました。

var=$(ssh $host test -f /file \&\& pipeline1 \|\| pipeline2)

同様に、pipeline1の最後のコマンドは||その引数と見なされます。


私も以下で試しました(これ)、ランニング:

do_this () {
  if [ -f /file ]; then
    pipeline1
  else
    pipeline2
  fi
}
var=$(ssh $host "$(set); do_this")

しかし、私の変数には影響しませんが、私のスクリプトは見苦しい不要なエラーメッセージを出力します。

bash: line 1: BASHOPTS: readonly variable
bash: line 8: BASH_VERSINFO: readonly variable
bash: line 24: EUID: readonly variable
bash: line 71: PPID: readonly variable
bash: line 82: SHELLOPTS: readonly variable
bash: line 92: UID: readonly variable

どんな提案がありますか?


修正する

私のパイプラインが何であるかを含める必要があると思います。基本的に、それはテキスト処理バンドルです。

cat file | grep "something" | sed 's/.*="\(.*\)"/\1/' | tr ' ' '-'

の回答に基づいてJechiselつまり、コマンドを一重引用符で囲む必要があります。

var=$(ssh $host 'if [ -f /file ]; then cat file | grep "something" | sed 's/.*="\(.*\)"/\1/' | tr ' ' '-' ; else cat otherfile | ... ; fi'

私は持っていますtr: invalid option -- ';'。パラメータtrとして使用されます。;


動作方法は次のとおりですheredoc

var=$(ssh $host <<-EOF
  if [ -f file ]; then
    pipeline1
  else
    pipeline2
  fi
EOF
)

しかし、sedで使用されている正規表現はvimの色を損なうでしょう。heredoc今はこの答えを受け入れます。


アップデート2:私の質問は重複していないようです。sshpassの複数のコマンド、私の状況はより具体的で、他のスレッドは一般的に尋ねました。

答え1

存在する:

ssh host code

ssh実際に引数として渡したコードを解釈するために、シェル(ターゲットユーザーのログインシェル)を実行します。複数の引数が指定されている場合、sshは引数をスペースで連結し、ユーザーのログインシェルにhostそれを再解釈させます。

一般的に言えば、合格したい一つ単一のコード引数を選択しssh、ローカルシェルが拡張されないように単一引用符で囲む必要があります。

リモートユーザーのログインシェルがBourne / POSIXに似ていることがわかっている場合は、次のことを行う必要があります。

var=$(ssh "$host" '
  if [ -f /file ]; then
    pipeline1
  else
    pipeline2
  fi'
)

リモートで解釈するコードに一重引用符を含める必要がある場合は、それらを挿入する必要があります'\''(一重引用符の保持、引用符付き(バックスラッシュを使用)一重引用符の挿入、一重引用符の再入力)。

リモートユーザーのシェルを保護せずに標準入力を介してリモートコマンドにデータを渡す必要がない場合(そしてSSH接続以外にリダイレクトされない限り、リモートコマンドは標準入力から読み取られない)、次のことができます。

ssh "$host" sh << 'EOF'
  if [ -f /file ]; then
    pipeline1
  else
    pipeline2
  fi
EOF

最初のトピックを参照して、EOFこのドキュメントでローカルシェルが拡張されないようにします。shスクリプトを書くために使用する構文を知るために、標準入力のコードを解釈するために明示的な呼び出しを行います。

このアプローチでは、単一引用符をエスケープする必要もありません。

あなたの

cat file | grep "something" | sed 's/.*="\(.*\)"/\1/' | tr ' ' '-'

次のように書くことができます:

<file sed '/something/!d; s/.*="\(.*\)"/\1/; y/ /-/'

だからこれは私たちに次のことを与えます:

ssh "$host" '
  file=/path/to/some/file
  otherfile=/path/to/some/other/file

  if [ -f "$file" ]; then
    <"$file" sed '\''/something/!d; s/.*="\(.*\)"/\1/; y/ /-/'\''
  else
    <"$otherfile" ...
  fi'

(リモートユーザーのログインシェルがBourne / POSIXのような構文と異なる構文を持つcsh、tcsh、fish、rc、es、akangaの場合、このコマンドは機能しません。)

または:

ssh "$host" sh << 'EOF'
  file=/path/to/some/file
  otherfile=/path/to/some/other/file

  if [ -f "$file" ]; then
    <"$file" sed '/something/!d; s/.*="\(.*\)"/\1/; y/ /-/'
  else
    <"$otherfile" ...
  fi
EOF

答え2

数年前も同様のことをしましたが、こうしました。

まず、終了ステータスをテストしてください。

ssh -t remoteuser@lremotehost 'if [[ -e /etc/fstabs ]]; then exit 0; else exit 127; fi' >/dev/null 2>&1

終了ステータスを確認してください。

echo $?

出力127はない/etc/fstabsので/etc/fstab

これで、末尾がないことを確認する/etc/fstabsように変更します。/etc/fstabs

ssh -t remoteuser@lremotehost 'if [[ -e /etc/fstab ]]; then exit 0; else exit 127; fi' >/dev/null 2>&1

終了ステータスをもう一度確認してください。

echo $?

出力はリモートシステムに0出力があるためです。/etc/fstab

その後、変数に入れて終了ステータスを確認し、それに基づいてスクリプトを実行します。また、終了ステータスを変数に保存します。

var=$(ssh -t remoteuse@remotehost 'if [[ -e /etc/fstabs ]]; then exit 0; else exit 127; fi; exec bash -li' >/dev/null 2>&1); pid=$?

case $pid in
   0) echo 'good!';;
   *) echo 'bad!' >&2;;
esac

これが私がこの状況で生き残った方法です。完璧な解決策とは言えませんが、確認してみてください。ただし、SSHキーをリモートホストに渡したため、SSH経由でログインしてもパスワードは確認されません。私も両方のコンピュータでログインシェルとしてbashを使用していますが、これは私が次exec bash -liの理由です。

関連情報