Linux系のコマンドを扱っていると対話型のコマンドが多いので 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")
よく見かけるサンプルは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で指定しているので、 実行までに5秒かかる場合は、exceptが失敗している可能性が濃厚です。
対話が必要なアプリはspawnで実行するのがコツなようです。
#はプロンプトなので、スーパユーザならば#だろうしか一般ユーザならば$がよくつかわれる。
ただ、プロンプトは設定で変更できるので、確認は必要。
シェルに成功したのか失敗したいのか返したいとき
exit 0
exit 1
シェル側はこんな感じ
if exceptのシェル then else fi
http://www.ibm.com/developerworks/jp/linux/library/l-sc1/
http://ja.wikipedia.org/wiki/Tcl/Tk
http://expect.sourceforge.net/
http://pyomeha.blog42.fc2.com/blog-entry-6.html
DejaGnu?は他のPCをテストするフレームワークです。Expectで書かれており、すなわちTclで書かれているわけです。
http://www.gnu.org/software/dejagnu/
TclのかわりにPythonで書かれたライブラリかも http://expectpy.sourceforge.net/
http://rakuto.blogspot.jp/2006/09/rubyexpectrb.html
http://www.42klines.com/2010/08/14/what-to-expect-from-the-ruby-expect-library.html
http://oshiete.goo.ne.jp/qa/4864602.html
http://www.webhtm.net/blogger/2008/10/rubyexpect-expectrb.html
http://www.ruby-lang.org/ja/old-man/html/expect.html
http://php.net/manual/ja/expect.examples-usage.php
http://expectj.sourceforge.net/
運用を定型化したほうがよい。たとえば実行した結果をwiki等に反映させるとか。
定型化するときに、対話が必要ではない箇所はシェル化しておいたほうがよい。
理由はexpectのスクリプトを書くよりも、シェルを書いたほうが楽
パスは絶対パスでかいておいたほうがよい。理由は、expect内では、シェルの変数とかが使えなかったりするから。
exceptをつかうファイルは普段は暗号をかけ、アクセスできる権限を絞っておくことにしたほうがよい
[Linux] ファイルを暗号化する方法あれこれ
http://ameblo.jp/itboy/entry-10484615897.html
テキストファイルに暗号化をかけずにパスワードで保管しているのはよくないので自主的に脆弱性について検査しておくこと
http://dropsafe.crypticide.com/article/1019
#!/usr/local/bin/expect -f
(パスは環境によって変わります。)
set timeout 10
spawn telnet 10.1.1.1 #expectコマンド内でtelnetを実行
expect "Password: "
send -- "aaa\n"
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
# 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
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
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 $time
この辺は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 {[llength $argv] != 1} {
puts stderr "Usage: daytime host"
exit 1
}
プロセスをやめたり、名前付きのプロセスにリモートで再接続することができるかも
ディレクトリ内のファイルを暗号化するのにつかえるのかも
decryptdir
2人で1つのプロセスを触る時につかうのかも
パスワード自動生成
複数台のDBのパスワード変更をおこなうことができるかも
おしらせがあったらポップアップウィンドウを開くのかも
出力をみたくない場合につかうのか?
1つのプロセスを複数のターミナルで扱うときにつかうのかも
後々知ったことではあるが SSHではRuby/PythonなどのSSHライブラリを使うべきで、パスワードではなく証明書ログインをするべき。
だが、やりすぎると、自分の家の中のドアに全部鍵をつけるがごとくになりかねないので、そこはバランスをとろう
自分用のforkした