想定されるケース
・sshしてコマンドを実行するところまで自動化したい
・公開鍵認証ではなく、パスワード認証になっている(非推奨)
・パスワード認証も自動で突破したい
・expectを採用するしかない
・sshで実行したコマンドの戻り値を、シェル内で使用したい
こんな限られた場面・環境でシェルの作成を余儀なくされた...。
そんなあなたのために書き残します。(当然筆者も含まれます)
結論
以下のようなコードを書けば実現できます。
#!/bin/bash
expect -c "
set timeout 10
spawn -noecho ssh ${USER}@${REMOTE_HOST} \"${COMMAND}\"
expect \"assword\"
send \"${PASSWORD}\"
interact
exit [lindex [wait] 3]
"
echo $? # ${COMMAND}の終了ステータスが出力される
ポイント
以下の記述が大事なところです。
exit [lindex [wait] 3]
・[ ]
:コマンド置換。$( )
と同様コマンドの実行結果に置換される。
・exit
:expectコマンドを終了するの意です。
・lindex
:関数。lindex [配列] [index]
と引数を指定して記述すると、indexに対応する配列の要素を取得できる。
・wait
:少しややこしいので、manコマンドの記述を見てみましょう。
spawnされたプロセス(あるいは、名前つきのプロセスがなければ現在のプロセス) が終了するのを待つ。
wait は、通常4つの整数のリストを帰す。
最初の整数は、終了を待ち構えているプロセスの pid である。
2つめの整数は、関連するspawn idである。
3 つめの整数は、オペレーティングシステムエラーがあれば -1、 そうでなければ、0である。
3 つめの整数が 0 であれば、4 つめの整数はspawnされたプロセスからのリターン コードである。
3 つめの整数が -1 であれば、4 つめの整数はオペレーティングシステム によって設定された errno の値である。グローバル変数 errorCode も設定される。
結局、ややこしいわ
というわけで、要点だけまとめてみました。
・ spawnされたプロセスの終了後に通常4つの整数のリストを帰す
・ 4つめの整数はspawnされたプロセスからのリターンコードである
これで、少しは理解が進んだのではないでしょうか。
コマンドをもう一度眺める
ここでexit [lindex [wait] 3]
をもう一度見てみましょう。
以下のような流れで変換されていきます。
exit [lindex [wait] 3]
↓ waitがコマンド置換されて配列に置き換わる。4番目に終了ステータスが入っている。
exit [lindex (pid spawnid OSerr exitcode) 3]
↓ lindex関数もコマンド置換され、4番目の要素(index=3)である終了ステータスを取得し置き換わる。
exit "exitcode"
最後に
最初に述べたように、想定されるケースはかなり狭いです。
sshでは鍵認証を使用すべきで、パスワード認証を自動化するためにexpectを用いること自体非推奨です。
(そもそもパスワードの扱いに困ります。平文でどこかに記載するなんてのは厳禁です。)
「構成・環境の変更なんて今更できない!制約がある中でなんとかしないといけないんだ!」という方のある種"特効薬"になれば幸いです。
ここまで読んでいただきありがとうございました。