趣旨

Linux系のコマンドを扱っていると対話型のコマンドが多いので expectをつかった自動化についてまとめる。

目次

expectとは

expectはTclの拡張ライブラリで、対話型の入力が必要な時の自動実行ツールで有名です。Tclのプログラムがそのまま使えるらしいのです。

手順

http://d.hatena.ne.jp/nakaya0611/20100407/1270642553

自分なりのコツ

スペースの取り扱いが大切というか特殊

スペース文字の取り方が重要です。 下記は、メソッド名ですが、Javaとかの感覚でスペースをつけたり、外したりすると動きません。

proc sum {arg1 arg2} {
   set x [expr {$arg1 + $arg2}];
   return $x
} 

puts " The sum of 2 + 3 is: [sum 2 3]\n\n"

文字列のエスケープ

例を下記に書いておきます。

元にしたスクリプトのファイル

for num in ${list[@]};do
table=${num}
file=${table}_dump.sql
mysqldump --opt -c -u root ozaka $table > $file
pass03=program3
pc03=program3@192.168.1.119
send=~/deployscripts/expectscripts/send.expect
$send $file $pc03 $pass03
done

エスケープするとこうなる

set command03 ""
append command03 for\ num\ in\ \${list\[@]}\;do "\n"
append command03 table=\${num} "\n"
append command03 file=\${table}_dump.sql "\n"
append command03 mysqldump\ --opt\ -c\ -u\ root\ ozaka\ \$table\ >\ \$file "\n"
append command03 pass03=program3 "\n"
append command03 pc03=program3@192.168.1.119 "\n"
append command03 send=~/deployscripts/expectscripts/send.expect "\n"
append command03 \$send\ \$file\ \$pc03\ \$pass03 "\n"
append command03 done "\n"
append command03 "\n"

引数に配列を使う

例えば下記のようにシェルで配列を定義したとします。

list=( "aa" "bb")

この引数をtclで取り扱うサンプルコードを作ることができました。

コード

#!/usr/bin/expect

proc quoteList {list} {
  set ret ""
  for {set i 0} {$i<[llength $list]} {incr i} {
     append ret " \"" [lindex $list $i] "\""

  }
  return $ret
}

set m [quoteList $argv]
set mm "list=(" 
append mm $m ")"
puts $mm

使い方例

これをexpectのスクリプトに渡します。

ファイル名を仮にchangeTables.exceptとします。

changeTables.except $list

結果

list=( "aa" "bb")

シェルをspawnする

よく見かけるサンプルはsshのサンプルだったりしますが、シェルで時々パスワード聞かれて面倒な場合のスクリプトはシェルをspawnした方が良い。 通常のコマンドをやり取りするには、シェルをspawnした方がよい。

だいだいの雰囲気をつかむための例

#!/usr/bin/expect

set timeout 5

spawn /bin/sh

expect "#"

set my ""

send $my
expect "Enter password:"
send $PASS
expect "#"
send "exit\n"

expect eof

実行が5秒かかるならば、exceptが失敗している

タイムアウトを5で指定しているので、 実行までに5秒かかる場合は、exceptが失敗している可能性が濃厚です。

対話が必要なアプリはswanで実行する

対話が必要なアプリはspawnで実行するのがコツなようです。

#はプロンプトなので、スーパユーザならば#だろうしか一般ユーザならば$がよくつかわれる。

ただ、プロンプトは設定で変更できるので、確認は必要。

実行結果

シェルに成功したのか失敗したいのか返したいとき

成功

exit 0

失敗

exit 1

受け取り側の記述例

シェル側はこんな感じ

if exceptのシェル
then

else

fi

サーバー・クリニック: 期待以上の出来のExpect

http://www.ibm.com/developerworks/jp/linux/library/l-sc1/

Tclの文法などの概要(Wiki)

http://ja.wikipedia.org/wiki/Tcl/Tk

リンク

本家(英語)

http://expect.sourceforge.net/

Tcl

http://www.tcl.tk/

Tcl基礎文法最速マスター

http://pyomeha.blog42.fc2.com/blog-entry-6.html

マニュアルページ

http://www.tcl.tk/man/tcl8.5/

DejaGnu?

DejaGnu?は他のPCをテストするフレームワークです。Expectで書かれており、すなわちTclで書かれているわけです。

http://www.gnu.org/software/dejagnu/

ExpectPy?

TclのかわりにPythonで書かれたライブラリかも http://expectpy.sourceforge.net/

Rubyのexpect.rbの使い方

http://rakuto.blogspot.jp/2006/09/rubyexpectrb.html

http://www.42klines.com/2010/08/14/what-to-expect-from-the-ruby-expect-library.html

Ruby expect 条件文(if文)の書き方

http://oshiete.goo.ne.jp/qa/4864602.html

telnet自動化のサンプル

http://www.webhtm.net/blogger/2008/10/rubyexpect-expectrb.html

リファレンスマニュアル

http://www.ruby-lang.org/ja/old-man/html/expect.html

PHPをつかった例

http://php.net/manual/ja/expect.examples-usage.php

Javaの場合:ExpectJ 

http://expectj.sourceforge.net/

心構え

運用を定型化したほうがよい。たとえば実行した結果をwiki等に反映させるとか。

定型化するときに、対話が必要ではない箇所はシェル化しておいたほうがよい。

理由はexpectのスクリプトを書くよりも、シェルを書いたほうが楽

パスは絶対パスでかいておいたほうがよい。理由は、expect内では、シェルの変数とかが使えなかったりするから。

exceptをつかうファイルは普段は暗号をかけ、アクセスできる権限を絞っておくことにしたほうがよい

ファイルの暗号化

[Linux] ファイルを暗号化する方法あれこれ

http://ameblo.jp/itboy/entry-10484615897.html

脆弱性チェック

テキストファイルに暗号化をかけずにパスワードで保管しているのはよくないので自主的に脆弱性について検査しておくこと

Crack/Cracklib

http://dropsafe.crypticide.com/article/1019

逆引き

使用する言語の宣言をする

#!/usr/local/bin/expect -f   

               (パスは環境によって変わります。)

10秒間応答がなければ終了させる

set timeout 10

expectコマンド内でtelnetを実行

spawn telnet 10.1.1.1     #expectコマンド内でtelnetを実行

Password: 文字列が帰ってきたら

expect "Password: "

ログインパスワードaaaを入力

send -- "aaa\n"

expectの終了

expect eof

引数の確認

http://blogs.yahoo.co.jp/eguchium/50663261.html

複数の質問を想定

sshは1度目と2度目以降では質問がことなるので、その場合の記述例

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\"
}

ユーザの入力を促す

interact

サンプルコード

パスワードを更新するための簡単なExpectプログラム

# Invoke as "change_password <user> <newpassword>".

  package require expect
      # Define a [proc] that can be re-used in many
      #    applications.
  proc update_one_password {user newpassword} {
      spawn passwd $user
      expect "password: "
      exp_send $newpassword\n
          # passwd insists on verifying the change,
          #    so repeat the password.
      expect "password: "
      exp_send $newpassword\n
 }
 eval update_one_password $argv

Expectの自動化にGUIの外見をもたせる

package require Tk

  frame .account
  frame .password
  label .account.label -text Account
  entry .account.entry -textvariable account
  label .password.label -text Password
      # Show only '*', not the real characters of
      #    the entered password.
  entry .password.entry -textvariable password -show *
  button .button -text "Update account" -command {
      update_one_password $account $password
  }
  pack .account .password .button -side top
  pack .account.label .account.entry -side left
  pack .password.label .password.entry -side left

interactをつかったサンプル

set CTRLZ \e032
interact {
	 -reset $CTRLZ {exec kill -STOP 0}
	 \e001    {puts "you typed a control-A\en";
		  send "\e001"
		 }
	 $       {puts "The date is [exec date]."}
	 \e003    exit
	 foo     {puts "bar"}
	 ~~
}

putsをつかったサンプル

puts $time

この辺はTclのコマンドをしらべたほうがいい

Tclコマンド

http://www.tcl.tk/man/tcl8.5/TclCmd/contents.htm

正規表現をつかったサンプル

regexp {([0-9][0-9]:[0-9][0-9]:[0-9][0-9])} $expect_out(buffer) _ time

if文をつかったサンプル

if {[llength $argv] != 1} {
   puts stderr "Usage: daytime host"
   exit 1
}

未調査なコマンド

dislocate

プロセスをやめたり、名前付きのプロセスにリモートで再接続することができるかも

cryptdir

ディレクトリ内のファイルを暗号化するのにつかえるのかも

decryptdir

kibitz

2人で1つのプロセスを触る時につかうのかも

mkpasswd

パスワード自動生成

passmass

複数台のDBのパスワード変更をおこなうことができるかも

tknewsbiff

おしらせがあったらポップアップウィンドウを開くのかも

unbuffer

出力をみたくない場合につかうのか?

xkibitz

1つのプロセスを複数のターミナルで扱うときにつかうのかも

留意点

後々知ったことではあるが SSHではRuby/PythonなどのSSHライブラリを使うべきで、パスワードではなく証明書ログインをするべき。

だが、やりすぎると、自分の家の中のドアに全部鍵をつけるがごとくになりかねないので、そこはバランスをとろう

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS