#topicpath

* 目次 [#kd018898]
#contents

////////////////////////////////////////////////////////////////////////////////
* 共通 [#udbf4d6a]
** コメント [#m841fda8]
-- "#" 以降がコメントになる
--  式を書いた後にコメントを書く場合、 "#" の前に ";" がないとエラーになるので注意

////////////////////////////////////////////////////////////////////////////////
** I/O [#k5fe58b5]
- 定義済みの channel_ID: stdout / stderr / stdin
- 出力
-- puts [-nonewline] channel_ID string
--- defalut では stdout へ出力する。
--- -nonewline オプションをつけると、改行文字を省略する。
--- 例
 puts stderr "ERROR: an error has occuered\r";  # stderr に文字列を出力。
- 入力
-- gets
 gets stdin VAL;    # 変数 VAL に、stdin から読み取った内容を格納する。
-- read [-nonewline] channel_ID
--- channel_ID から全てのデータを読み込む。
--- -nonewline オプションをつけると、改行文字を省略する。
-- read channel_ID bytes
--- channel_ID から、bytes 分のデータを読み込む。
-- seek channel_ID offset [origin]
--- originは、以下が定義されている:
|~origin |~description |
|start |ファイルやデバイスの先頭から |
|current |現在のアクセス位置 |
|end |ファイルやデバイスの終端から |
-- tell channel_ID
--- channel_ID の、現在のアクセス位置を返す。


////////////////////////////////////////////////////////////////////////////////
** 1sec 未満の sleep [#n27d57ab]
- 1sec未満の sleep をするには、sleep ではなく、after を使用する。単位はmsecである。
 # 50msec の sleep
 after 50;


////////////////////////////////////////////////////////////////////////////////
** 変数 [#b8ea3b68]
- 宣言
-- 宣言・代入する時は、変数名に接頭辞をつけない
 set AAA "1234";
-- 参照する時は、接頭辞 "$" をつける。$変数名 とするか、${変数名} と表記する。
 puts "AAA: ${AAA}\r";
 puts "BBB: $BBB\r";
-- ※宣言されていない変数を参照しようとすると、スクリプトabortするので注意すること。

////////////////////////////////////////////////////////////////////////////////
** 関数 [#qf190abc]
- 関数定義
 proc function_name { arg1 arg2 } {
     # 実装
     return ${value};   # 返り値を返す場合
 }
-- 関数名は、キーワード "proc" に続けて書く
-- 仮引数には、接頭辞(?) "$" はつけない。区切りはコンマではなくスペース

- 関数呼び出し
 function_name ${aa} ${bb};                       # 通常の呼出しの場合
 set return_value [ function_name ${aa} ${bb} ];  # 返り値を取得する場合

** グローバル変数 [#cfce567f]
- グローバル領域に変数を定義し、その変数を参照する関数内で、 "global" 文にて列挙する必要がある
 #!/usr/bin/expect
 set G_VAL1 "";
 
 proc func_name { arg1 arg2 } {
     global G_VAL1;
     if { ${arg1} > 10 } {
         set G_VAL1 ${arg1};
     } else {
         set G_VAL1 ${arg2};
     }
 }


////////////////////////////////////////////////////////////////////////////////
** 他のスクリプトファイルから関数や変数を取り込む [#za16d2cc]
- あるスクリプト(script-main.expとする)から、別のスクリプトファイル(script-sub.expとする)の中身を取り込みたいときは、以下のように記述する:
 source "script-sub.exp";
-- これにより、script-sub.exp の定義内容が、元からscript-main.expに書かれていたかのように扱える。


////////////////////////////////////////////////////////////////////////////////
** フロー制御 [#hb9d792c]
*** if [#lf2b9949]
 set i 10;
 set EXP 20;
 if { ${i} > ${EXP} {
     処理...
 } elseif { ${i} == ${EXP} } {
     処理...
 } else {
     処理...
 }

- if 条件文内の or/and は、C/C++ 等と同様、 "||" "&&" で列挙する。

- 動かない例: 「 } elseif { } { 」ではなく「 elseif { } {」
 if { ${i} > ${EXP} } { 処理... }
 elseif { ${i} == ${EXP} } { 処理... }
 else { 処理... }


*** for [#q0f54d72]
 set LOOP_MAX 100;
 for { set i } { ${i} < ${LOOP_MAX} } { incr i } {
     処理...
 }

*** foreach [#xdb5adc3]
- 例1
 foreach i { 0 1 2 3 4 5 } {
     処理...
 }

- 例2
 foreach { i j } { 0 1 2 3 4 5 } {
     処理...
 }

- 例3
 foreach i { 0 1 2 3 4 5 } j { 0 10 20 30 40 50 } {
     処理...
 }

*** while [#j842bed1]
 while { ${i} >= ${VAL} } {
     処理...
 }

*** ループ制御の補助コマンド [#e483400d]
|~command |~description |
|continue | ループをスキップ。|
|break   | ループを終了 |
|exit [ return_code ] |return_code は、default では0 |


*** switch [#fd207646]
 switch [options] ${string} {
     pattern_1 {
         処理...
     }
     pattern_2 {
         処理...
     }
     ...
     default {
         処理...
     }
 }
- options
|~option |~description |
|-exact | string と pattern が完全に一致する場合に真となる |
|-glob  | string をグロブパターン pattern とマッチング |
|-regexp| string を正規表現 pattern とマッチング |
|--     | option 終了を示す |
- option 無しの場合、値 1 と 値 01 は、別物として識別される。

- 上記文例における ${string} は''あくまで文字列''で、数値として扱うと期待通りに pattern_* にマッチしない。


////////////////////////////////////////////////////////////////////////////////
** 時間 [#md0caab7]
- 現在時間の取得
 set time_stamp	[ clock seconds ];  # clock clicks -milliseconds ではダメ。
- 上記で取得した時間の値 time_stamp を書式化して、time に格納する。
 set time [ clock format ${time_stamp} -format {%s} ];
 set time [ clock format ${ts} -format {%Y-%m-%d %H:%M:%S} ];   # YYYY-MM-DD hh:mm:ss
- より詳しくは、[[Lang/Tcl_Tk_Expect/time]] へ。

*** 経過時間の計測 [#x561d74a]
- "-milliseconds" オプションを付けると、msecオーダーの精度が保証される。
 # 経過時間計測
 #   1. GetWrapTimeInit で start_ts を取得。
 #   2. GetWrapTime     を、 start_ts を渡して呼ぶと、現在時間との差分を返す。
 proc GetWrapTimeInit { } {
 	set start_ts	[ clock clicks -milliseconds ];
 	set start_ts	[ clock format ${start_ts} -format {%s} ];
 	return ${start_ts};
 }
 
 proc GetWrapTime { start_ts } {
 	set ts		[ clock clicks -milliseconds ];
 	set ts		[ clock format ${ts} -format {%s} ];
 	set wrap	[ expr ${ts} - ${start_ts} ];
 	return ${wrap};
 }


////////////////////////////////////////////////////////////////////////////////
** 変数の名前と値の取得 [#pd90a3f5]
- info コマンドを使う
- global 変数名を取得
 書式: [ info globals ?option? ]
 set gloval_val [ info globals ];    # 全てのglobal 変数の変数名リストが出来る。
 puts "${gloval_val}";
-- ?option? で文字列を指定すると、それに一致するものだけがリストに格納される(デフォルトは *)
 set global_val [ info globals *_library ];  # '_library' で終わる名前の全てのglobal変数が格納される。

- 変数名リストに格納された変数の「値」を知るには、for 文などで変数名リストを回して、それぞれの値を得る
 set num [ llength ${val_names} ];  # 変数名リスト val_names の要素数を得る
 for { set i 0 } { ${i} < ${num} } { set i [ incr i ] } {
 	set val_name	[ lindex ${val_names} ${i} ];
 
 	# set val_val		[ eval "set \$\{${val_name}\}" ];  # 間違い例:こんなことは出来ない。
 	# set val_val		${${val_name}};  # 間違い例:こんなことは出来ない。
 
 	set val_val		[ set ${val_name} ];  # これで値が取得出来る。
 
 	puts "\[name:${val_name}\]\[val:${val_val}\]\r";  # 表示
 }



////////////////////////////////////////////////////////////////////////////////
* expect [#f32570ee]
** 基本 [#ca535d79]
+ 相手にするプログラムを呼び出す
+ expect で、期待するキーワード(プロンプト文字列や、エラーメッセージの一部など)を指定する。
+ 期待するキーワードが来たときに、人に代わって入力するコマンド等を send で送る。
- 例:bashを起動し、カレントディレクトリのファイル一覧を表示する
 #!/usr/bin/expect
 spawn bash;           # 相手にするプログラムの一例として、ここでは bashを指定
 expect "\\$" {        # プロンプト文字列が "$" の場合は、こうなる(ちょっとややこしいが)
     send "ls -l\r";   # ユーザがTREURNキーを押すのに対応するものとして、コマンド文字列終端に改行 "\r" が必要。
 }


////////////////////////////////////////////////////////////////////////////////
** interact :ユーザのマニュアル操作も組み合わせる [#e0fec1ca]
 #!/usr/bin/expect
 spawn bash;
 expect "\\$" {
     send "ping www.example.com\r";
 }
 
 interact;    # ユーザーに処理を渡す

- ユーザのマニュアル操作も組み合わせる2:Ctrl-a を押すと、指定された処理を行う
 ・・・
 set CTRL_A "\001";
 
 interact {
     ${CTRL_A} {
         puts "typed Ctrl-a!!\r";
         処理...
     }
 }



////////////////////////////////////////////////////////////////////////////////
** [[Ctrl-* キーの割り当て>Lang/Tcl_Tk_Expect/Ctrl-キーマップ]] [#cb0ce827]
- リンク先のように定義してやれば、Ctrl-a と打ったときのアサインに使用出来る。



////////////////////////////////////////////////////////////////////////////////
** timeout [#m636720b]
- "expect" で待つときのタイムアウトは、変数 timeout に設定する。defaultでは10sec
 set timeout -1;   # -1 にすると、タイムアウトしなくなる。

*** タイムアウトやキーワード以外の条件を考慮した待ち方をしたいときの例 [#s2bb5fb2]
 set timeout 1;
 while {1} { # 基本的には無限待ち
     expect "KEYWORD" {
         処理;
         break;   # この処理が終わったら、whileループを抜ける
     }
     if { 条件 } { # timeout する度にこの条件がチェックされる
         break;    # 場合によっては無限待ちループを抜ける
     }
 }



////////////////////////////////////////////////////////////////////////////////
** expect [#cf2dedcd]
*** 複数のキーワードを受け付け、キーワードによって挙動を変えたい場合 [#e6ccd01b]
 expect {
     "KEYWORD1" { 処理1; }
     "KEYWORD2" { 処理2; }
 }
- エラー発生時にはエラーメッセージを出力して再度正常時と同様にプロンプトを表示するようなプログラムが相手の場合、プロンプト文字列(正常時処理用に待つキーワード)は一番最後に列挙する。



////////////////////////////////////////////////////////////////////////////////
** シグナルを受け取った時の処理 [#q1278609]
- bashのシェルスクリプトのように、"trap" コマンドを使う。
- 詳しいことは、[[expect の manpage>http://www.linux.or.jp/JM/html/expect/man1/expect.1.html]] にも書かれているので参照のこと。
 書式: trap [[command] signals]
- 次の例では、シグナルを受け取った時に、終期化処理を行う "terminate" 関数を定義し、それを呼び出すようにしている。
 proc terminate { } {
     # 例えば、ログ取得を終わらせる、とか。
     log_file;
 }
 
 trap terminate {SIGINT SIGTERM}



////////////////////////////////////////////////////////////////////////////////
** log の取得 [#n927a8bf]
- "log_file" コマンドとシグナルを利用すると結構便利に使える。
- 下記は、"interact" で制御をユーザに引き渡した後、 Ctrl-a でログ取得開始、Ctrl-b でログ取得終了となるようにした例。
- 下記は、"interact" で制御をユーザに引き渡した後、 Ctrl-a でログ取得開始、Ctrl-b でログ取得終了となるようにした例。C-c, C-d でSIGNALを受信し、ログファイルを close した上で終了する。
 proc LogStart { logfile } {
     log_file ${logfile};
     puts "Logging Start: ${logfile}\r;
 }
 proc LogStop { } {
     puts "Logging Stop\r";
     log_file;
 }
 
 proc terminate { } {
      LogStop;
      exit 0;
 }
 
 trap terminate {SIGINT SIGTERM}
 
 # 処理を色々記述。
 set CTRLA \001
 set CTRLB \002
 interact {
     ${CTRLA} { LogStart ${out_log}; }
     ${CTRLB} { LogStop; }
 }


////////////////////////////////////////////////////////////////////////////////
* 参考リンク [#laeb8a5b]
- [[もっとTcl/Tk>http://www.interq.or.jp/japan/s-imai/tcltk/index.html]]

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS