expectコマンドは、対話形式のコマンドを自動で実行したいといった場合に便利です。
例えば、SSHでサーバーに接続する際、パスワード認証が用いられている場合は当然パスワードを入力しないとログインできません。
または、何らかのソフトウェアをコマンドラインからインストールする場合に、その過程で幾つか環境の状態により質問されたり(パスを求められたり、ディレクトリ名を指定するなど)します。
こういったコマンドを自動化したい場合は、予めその実行コマンドにスキップするためのオプションなどが用意されていないと対応が難しかったりもしますが、expectコマンドを利用すればそれらのほとんどを回避することが可能になります。
SSHで接続してリモートサーバーのディスク容量を確認する
例えばサーバー運用者があるサーバーにログインしてディスク容量を定期的に確認しているとします。(あまり目視監視するってことも無いとは思いますが・・・)
サーバーにログインして、また別のサーバーにログインし・・・、となるとかなり手間にもなったりしますのでexpectコマンドでそれらの流れをまとめ、自動化するためのスクリプトを作ってみます。
#!/bin/bash expect -c " set timeout 10 spawn ssh -l work srv01 expect \"Are you sure you want to continue connecting (yes/no)?\" { send \"yes\n\" expect \"work@srv01's password:\" send \"password\n\" } \"work@srv01's password:\" { send \"password\n\" } expect \"\[work@srv01 ~\]$\" send \"df -k\n\" send \"exit\n\" interact "
流れを簡単に説明すると、最初のexpect -cオプション以降でこれから実行するコマンドを指定します。
実際にコマンドを実行する箇所はspawnを記述した箇所(SSH接続)になります。
spawn ssh -l work srv01
上記で、workユーザーでサーバーsrv01へ接続しています。
次のexpectは、コマンドを実行した際に標準出力に表示されるメッセージを指定しています。
expect \"Are you sure you want to continue connecting (yes/no)?\" {
最初にSSH接続した場合は、リモートサーバーの公開鍵を保存するか質問されるので、もしこのメッセージが表示されたら、sendオプションで「yes」を送信するように指定しています。
公開鍵の取り込みが終わったらパスワードが求められるので
expect \"work@srv01's password:\"
と、workユーザーのパスワード求めるプロンプトが表示されたら
send \"password\n\"
sendオプションでworkユーザーのパスワードを自動で渡すように指定しています。
このようにexpectコマンドは、基本的にexpectで期待するメッセージ、そしてsendオプションでそれに対しての入力内容をセットしていきます。
また、2度目以降のSSH接続の場合は、いきなりパスワードを求められるのでその場合のパターンも用意しています。
} \"work@srv01's password:\" { send \"password\n\" }
これは、プログラムのIF文と同様で条件分岐させて、幾つかの質問されるパターンを予め用意しておくことで、spawnで実行したコマンドの自動処理に柔軟性を持たせることが可能になります。
[work@localhost workspace]$ ./expect.sh spawn ssh -l work srv01 work@srv01's password: Last login: Tue Oct 19 23:50:09 2010 from localhost.localdomain df -k exit [work@srv01 ~]$ df -k Filesystem 1K-ブロック 使用 使用可 使用% マウント位置 /dev/vzfs 10485760 1036888 9448872 10% / none 4150972 4 4150968 1% /dev [work@srv01 ~]$ exit logout
サーバー切断までの処理を自動化できるため、リモートサーバーに接続してコマンド入力などの一連の流れを一つのシェルスクリプト実行だけで済ませることができます。
SCPを使ったファイル転送の自動処理
定期的に特定のファイルやディレクトリをリモートサーバーに転送(または取得)したいという場合の一連のコマンドを自動化してみます。
#!/bin/bash USER=work PASS=password HOST=srv01 FROM_DIR=/home/work/workspace/messages.log TO_DIR=/home/backup/ expect -c " set timeout 10 spawn scp $USER@$HOST:$FROM_DIR $TO_DIR expect \"Are you sure you want to continue connecting (yes/no)?\" { send \"yes\n\" expect \"$USER@$HOST's password:\" send \"$PASS\n\" } \"$USER@$HOST's password:\" { send \"$PASS\n\" } interact "
リモートサーバー(srv01)上にあるmessages.logというファイルをローカルサーバーにダウンロードします。
シェルスクリプトと組み合わせて実行することもできるため、今回はユーザー名やパスワード、転送先などの情報を変数に格納して実行しています。
逆のパターン(リモートサーバーへファイルを転送したい)は、scpのオプションを入れ替えるだけで対応できるでしょう。
$ ./scp-get.sh spawn scp work@srv01:/home/work/workspace/messages.log /home/backup/ work@srv01's password: messages.log 100% 26KB 25.5KB/s 00:00
もちろんこのシェルスクリプトは、パスワードを含むためにセキュリティ上あまりよろしくありません。
SSHやSCPをパスワードなしで転送したい場合は、リモートホストとホストベース認証や公開鍵認証方式を設定しておきパスワードなしでログインできるようにしておいたほうが良いかもしれません。
(そもそもホストベース認証ができるのであれば、わざわざexpectコマンドを利用する必要はありませんけど・・・)
設定方法は、関連記事内のエントリを参考にしてみてください。
いつもやっているあの作業だけど、幾つか環境の状態によって入力する情報が違うんだけど・・・って場合なんかに便利に使えますね。
運用・保守のいつもの作業もかなり効率化できるのではないでしょうか。
[PR]SSLサーバ証明書なら[ドメインキーパー]◆VeriSign/GlobalSign/他取り扱い
[PR]固定IPが【業界最安値クラス】+【5分で発行】+【2ヶ月無料体験】
関連記事
Linux上でファイルを削除してしまった際にさっくり復活できるextundelete
rsyncのバージョンをあげたらrsync経由の同期のバッチが動かなくなった件
inotifyを使ってファイルやディレクトリに起きたイベントを簡単に監視する
不要なファイルやディレクトリを削除できる「tmpwatch」コマンド