システム管理者必携?、悪魔のツール「Expect」

 ExpectはTclの拡張ライブラリですが、 Tclそのものと同じくらいそれはそれは長い長い歴史を歩んできたライブラリで、 Tclの最初のリリースの直後に既に原型が生まれています。 最新のバージョンは5.41という名前が知られているのでこれだと思います。 対応するTclのバージョンは8.0p2以降最新まで。 最新のTcl 8.4.6でも別に何事もなくコンパイルできました。 逆に言うと、Expectの機能はTcl 8.0p2の水準で既に完成領域にあるといってもよいでしょう。

Expectとは?
 職場でサーバ管理、ネットワーク管理などをしているシステム管理者の皆さんにお聞きしましょう。 深夜に各サーバにtelnetしたりアカウントを変更したりしながら行う定型操作を自動化したいと思ったことはありませんか?

しーん

コホン…
皆さん、ウソはよくないな…
はいとおっしゃい!(ピシピシ)

あー、そのように日々悩んでおられる皆さんに朗報朗報、 Expectは、UNIXのコマンドラインで対話的に実行されるアプリケーションの 「受け答え」を自動化するツールです。 対話的なアプリケーションを幾つか挙げると、 「telnet」「ftp」「passwd」「su」などがあります。 これらを自動化できるとどのようなことができるようでしょうか。 とある書籍風にケーススタディをやってみましょう。

「おいジョン、何を悩んでるんだい?」
「これを見てくれよ、マークのやつ、 Webの管理ツールを別のアカウントを作ってインストールしちまったんだ。 一時ファイルを消すのにいちいちsuしなくちゃいけない」
「仕方ないよ、パッケージツールなんだから」
「だけどこの処理を夜中に10個のファイルに繰り返すだろう? 処理を流してsuして流してsuしてを繰り返すんじゃ、俺は寝る暇がないよ」
「そんなときにはほら、expectさ。こんな感じに組めば、suも自動化できるんだ」
「ワーオ、すごいじゃないか、これなら今日から安心して眠れるぞ」
どうです。こんな感じに皆さんの生活も改善するわけです。
ん、なんかさっきから全然本題に入っていないような気がするのですが、 皆さんもそう思われますか?

はーい、はーい、はーい

コホン…
皆さん、ウソはよくないな…
いいえとおっしゃい!(ピシピシ)
そんなわけで(帰れ)

データベースの上げ下げを自動化してみよう
 上のジョンの例ではないですが、ここではsuコマンドで他のユーザになり、 そのユーザで何か処理を行って、またもとのユーザに戻る、という処理を Expectで自動化してみます。
 こうした作業の代表的なケースが、「データベースサーバの上げ下げ」です。 データベースサーバは、一般に各データベース専用のユーザアカウントを作ってインストールします。 そして(rootでなく)そのユーザでないと起動できなくなっているのが普通です。 ここではそのようなデータベースのひとつ、PostgreSQLの起動を行います。

#! /usr/bin/expect
spawn su - pgmaster
set prompt ".*ushi4%.*"

sleep 1
expect "Password: "
send "pgpass\r"
expect -re $prompt
send "postmaster -iS\r"
expect -re $prompt
send "exit\r"
expect eof
puts "OK: ***$expect_out(buffer)***"
# end.

 このスクリプトを実行すると、PostgreSQLの管理ユーザpgmasterにsuコマンドでスイッチして (パスワードはpgpass)、PostgreSQLのデータベースサーバを起動するpostmaster コマンドを実行して、抜けます。
 ナニをやっているのかはほんとに簡単なのですぐ分かると思います。 spawnはexecと同様、子プロセスを作って実行させます。 expectは、 相手のアプリケーション(子プロセス)から、 パターンにマッチする応答が返ってくるのを待ちます。 (-reというのは、パターンを正規表現で指定していることを知らせるオプションです) sendは、子プロセスに入力文字列を送りつけます。 (ラストに改行文字\rをつけるのを忘れずに) なお、expectコマンドはもっと多様な使い方ができ、 たとえば

expect "OK."    { puts "OKです。" } \
       "Error." { puts "エラーじゃあ" }
このように、応答パターンとそれが返ってきたときの処理を対にして複数指定することで、 OK処理、エラー処理、などの場合分けが可能です。
またこのような対話的な処理を終わらせるために、最後は 「send "exit\r"」とか「send "bye\r"」のように終了コマンドを送り、 相手も終了したことを「expect eof」で確認する処理となるのが普通です。

 さて、PostgreSQLの起動が自動化できたなら停止もできないはずはありません。 やってみそやってみそ。

#! /usr/bin/expect
spawn su - pgmaster
set prompt ".*ushi4%.*"

sleep 1
expect "Password: "
send "pgpass\r"
expect -re $prompt
send "pg_ctl stop\r"
expect -re $prompt
send "exit\r"
expect eof
puts "OK: ***$expect_out(buffer)***"
# end.

telnet、リモートコマンド実行も自動化してみよう
 大規模な基幹業務システムでは、 お茶の間で楽しんで書いているときには考えられないようなバッチ処理が必要になることがあります。 先ほどのジョンの例ではないですが、データベースから経営計画情報をダウンロードして Web化してWebサーバに転送し、そのサーバに入って専用のツールを使って可視データ化した後、 一時ファイルを消す、といった処理を支社店別に夜間に数回繰り返す、 という運用を考えると、 先行-後続関係を守りながら複数サーバで処理を行わせるには、 専用バッチジョブスケジューラを用意して緻密な時間設定を行う必要がありました。 しかしExpectを使うと下のような簡単なスクリプトで、 リモートサーバにtelnetしてコマンドを実行させることが可能になります。

set pid [spawn telnet falcon.center.nsnhnkmmkk.co.jp]
set prompt ".*faladmin@falcon%.*"

expect "login: "
send "faladmin\r"
expect "Password: "
send "falpass\r"
expect -re $prompt {
  send "/export/home/faladmin/dobackup.sh > /var/log/aaa.log\r"
}
expect -re $prompt
puts "OK: ***$expect_out(buffer)***"
send "exit\r"
expect eof

 実際、ExpectはTclと同様緩やかなライセンスになっているので、 FTPやtelnetを自動実行するためにExpectを使っているパッケージソフトも多数存在します。 例えば発注情報をインターネット(FTP)経由で転送するEDI(Electronic Data Interchange)ツール、 リモートデータベースのバックアップを集中管理するバックアップソフトなど、 業務基幹系の深いところで生き続けたソフトウェアといえます。 私たちも実際に使ってみればみるほど、その便利さに気づくことでしょう。

タイムアウトの設定について
 expectのデフォルト設定では、子プロセスの終了までの待ち時間が約20秒になっており、 子プロセスがこれ以上かかるとHUPシグナルで子プロセスを強制終了させてしまいます。 この時間を長くとるには、グローバル変数timeout にタイムアウトまでの時間を秒で指定しておきます。

set timeout 600

インストールの方法(UNIX)
 あっ!インストール方法が使用法の紹介の後に来ているのはなぜですか? それはですね、上に書いたように業務基幹系でよく使われるという事情があって、 私が触ったことのあるセンターサーバには最初からExpectが入っていることが多かったのです… Expect Home Page からUNIXの場合はexpect.tar.gzをダウンロードします。 Windows用にはネイティヴャなバイナリ配布が用意されているほか、 ActiveStateのActive Tclには標準で同梱されています。

ここではUNIXでのインストール方法だけをご紹介します。

% tar xozf expect.tar.gz
% cd expect-5.41
% sh configure --with-tclinclude=../tcl8.4.6/generic
% make
# make install

 Expectも基本はconfigure、make、make installですが、 Expectはコンパイル時にTclのソース配布に含まれる 「tclInt.h」というヘッダファイルを必要とします。 これは/usr/local/includeなどインストール位置にはないので、 面倒ですがTclのソース配布を展開し、そのgenericディレクトリを上のように --with-tclinclude オプションで指定してやります。
 Tcl本体と同様、Expectもconfigureの --prefixオプションでインストール位置を変更することができ、 そのデフォルトは/usr/localです。また、Tclの設定を $PREFIX/lib/tclConfig.sh というファイルから読み込もうとするので、 Tclを変わった位置にインストールしている場合は、このtclConfig.sh が置かれているディレクトリを--with-tclオプションで指定する必要があります。

拡張レビュー分室 top
(first uploaded 2002/03/09 last updated 2004/07/18, MISUMI URANO - YUKO AMEMIYA)