web-dev-qa-db-ja.com

それ以外の場合にラップされた複数のコマンドを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

プロセスが長くなるからです。代わりに、リモートマシンでifelseを実行したいと思います。


私はいくつかのアプローチを試しましたが、運がありませんでした。

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

これに基づいて answer は機能しますが、pipeline1の最後のコマンドは、引数としてelseとpipeline2の残りの部分を想定しています。

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の最後のコマンドは||を引数と見なしました。


私も以下を試しました( this に基づいて)、これは機能しています:

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 ' ' '-'

Jetchisel からの回答によると、要するに、コマンドを一重引用符で囲む必要がありました。

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
annahri

に:

ssh Host code

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

一般に、one単一コード引数をsshに渡し、ローカルシェルによって展開が行われないように、単一引用符で引用されていることを確認します。

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

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

リモートで解釈されるコードに一重引用符を含める必要がある場合は、それらを'\''として挿入する必要があります(一重引用符を残し、引用符で囲まれた(円記号付き)一重引用符を挿入し、一重引用符を再入力します)。

リモートユーザーのシェルを保証できず、stdinを介してリモートコマンドにデータを渡す必要がない場合(および、他の何かにリダイレクトされない限り、リモートコマンドがstdinから読み取ることはありません) ssh接続よりも)、代わりに行うことができます:

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

最初のEOFを引用することにより、ローカルシェルによってヒアドキュメントで展開が行われないようにします。また、明示的にshを呼び出して、stdinのコードを解釈するため、スクリプトを記述する構文がわかります。

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

きみの

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'

(リモートユーザーのログインシェルが、たとえばcsh、tcsh、fish、rc、es、akangaであり、その構文がBourne/POSIXのような構文と異なる場合は機能しません)

または:

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/fstabに変更します。末尾にsがないことに注意してください。

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があります。

0
Jetchisel