======================================================================= Tcl 文法メモ (C) Susumu ISHIHARA 2000 Jan 27 【参考文献】 - 浅野, 初めての Tcl/Tk, 技術表論社, 1995 この本にはバグが多い... - Tcl manページ セクション n に Tcl の各コマンドのマニュアルがある セクション 3 の Tcl_* というマニュアルエントリは,C とのインタフェー ス用の関数の解説である。 ======================================================================= ■コマンドインタプリタ tclsh (/usr/bin/tclsh) ■ まずは Hello World! puts "Hello World!" ※古いコマンド put はスクリプト中では使えなくなった \n, \t などが文字列中で使用可能 ■基本 文: 改行またはセミコロン (;) で区切られる 数字 数字なども一度は文字列として扱われる 単語の区切りは空白 基本的な文法は C や Perl よりもシェルに近い! ■数字と式 expr コマンドを使用する expr 1 + 1 expr 1+2 expr 3*4 expr 4 + 5 * 6 expr (4+5)*3 expr sin(3.14) expr 3/0 ---> divide by zero 数値の型: int と float (C の double) のみ 演算子: C と同じ -, +, ~, ! *, /, %, +, -, <<, >>, &, ^, |, <, >, <=, >=, ==, !=, a?b:c 関数: acos cos hypot sinh asin cosh log sqrt atan exp log10 tan atan2 floor pow tanh ceil fmod sin abs rand round double srand int ●その他の演算コマンド append var val [val..] ;# 変数 var にすべての val の値を追加 ;# 文字列としてつなげる incr var [val] ;# 変数に 1 (または val)を加算 glob opt pattern [pattern..] ;# ファイル名のグロブ ■文字列 空白がなければ,単純に書くだけで良い 空白があれば,"" または {} で囲む {} には「変数展開の抑制」機能がある→重要! "" 内では \ は展開される,{} 内では \ は展開されない "" 内では変数展開が行われる,{} 内では行われない \ による特殊文字が利用可能(クォートの必要はない) 行末の \ は一つの文を複数行にわかち書きをするのに用いられる。 文字列の比較: expr {"abc"} > {"def"} ;# "abc" と "def" を比較。{} が必要 ■単純変数 変数は [0-9a-ZA-Z_]+ ([a-zA-z][0-9a-zA-Z_]* ではない) 変数を参照するときには $ を変数名の前につける (like sh) 変数のセット・解除: set, unset set A A # 変数 A に文字列 "A" を代入 set B A # 変数 B に文字列 "A" を代入 set B $A # 変数 B に変数 A の内容 "A" を代入 set c "AB C" # 変数 C に文字列 "AB C" を代入 set A expr 1 + 2 ■配列 基本的に一次元配列 基本形式: 配列名(添字) 添字には文字列を使用可能(awk や Perl の連想配列と同じ) 添字を複数変数の合成文字列にすることによって,仮想的に多次元配 列を利用可能 set a(1) 10 set b("10") 100 set a($i,$j) 123 set a(1,2) 123 ■コメント 行の先頭に # or ;# 以降をコメント ※ 行の途中に # をいれただけではコメントにならない! ■式の評価 評価の前に変数の内容が展開される! →展開して得られた文に対して評価を行う 展開・評価は内側の括弧から行われる puts "hello world!" set $hello "hello world" puts $hello set a 3 ;# a に 3 を代入 set b $a.14 ;# b に 3.14 を代入 set b $a14 ;# これはうまくいかない ;# 変数 a14 を b に代入している set c {$a} ;# c に文字列 "$a" を代入 set c \$a ;# c に文字列 "$a" を代入 set a "3." set b ${a}14 ;# 変数名の明示 b は 3.14 になる set c {\t} ;# c に \t という文字そのもの(タブではない) ;# をいれる ■ [] による強制評価 ※シェルの `` と同様 set b [expr $a + 3] ;# b に $a + 3 の結果を代入 set a 5 set b [expr $a + 3].14 ;# b に 5.14 を代入 ■リスト 文字列を空白で並べたもの リストの作成・結合 (concat) concat {pine apple orange} {grape tomato} → pine apple orange grape tomato set a {pine apple orange} set b {grape tomato} concat $a $b → pine apple orange grape tomato "$a $b" → pine apple orange grape tomato リストの代入 set a [concat $a $b] 構造を保ったままのリストの作成 (list) set a {{pine apple} orange} set b {grape tomato} list $a $b → {pine apple} orange grape tomato 文字列からリストへの変換 (split) split {pine|apple|orange} "|" → pine apple orange 要素の抜きだし (lindex) set a {{pine apple orange} grape tomato} lindex $a 1 ;# 1 番目の要素をとりだす(インデックスは 0 から) → grape 要素の抜きだし (lrange) set a {pine apple orange grape tomato} lrange $a 1 3 ;# 1 番目から 3 番目の要素を取り出す → apple orange grape 要素の変更 (linsert) set a {pine apple orange} set b {grape tomato} linsert $a 1 $b ;# a の 1 番目の要素となるところに b を挿入 → pine {grape tomato} apple orange linsert $a 1 GRAPE TOMATO → pine GRAPE TOMATO apple orange ※要素の置換には lreplace を使う。要素の削除には lreplace で追 加するリストをなしにする ※リスト演算要コマンド一覧 (リストは L で表す) concat L1 L2.. 複数のリストを 1 レベルのリストに結合 list v1 v2.. v1, v2.. からなるリストの作成(構造を保つ) join L s 分離記号 s でリストの要素を結合 lappend L v1 v2.. v1, v2... の各要素をリストにして list に追加 lindex L i i 番目の要素の抜きだし linsert L i v1 v2.. i 番目の要素となるように v1, v2 を挿入 llength L リストの要素数を得る lrange L f e f 番目から e 番目までの要素の抜きだし lreplace L f e v1 v2.. f 番目から e 番目までを v1, v2.. と置換 lsearch opt L pat リスト中から pat に一致する要素を抜きだし opt: -exact, -glob(デフォルト), -regrep lsort opt l リストの要素の整列 opt: -ascii (デフォルト) -integer, -real -command command -increasing (デフォルト) -decreasing split s c 文字列を区切り文字 c でリストに分割 ■制御構造 ● if if {$a > 1} { puts "greater 1" } elseif {$a > 0} { puts "greater 0" } else { puts "less equal 0" } ※ { は if, elseif, else と同じ行に書く (実行部分が単文ならば {} は不要) ※ 条件は {} で囲む ※ elseif は else if でもないし,elsif でもない ● while while {条件} { # ループ内コマンド } ※ { は while と同じ行に書く (実行部分が単文ならば {} は不要) ※ 条件は {} で囲む ● for for {set i 1} {$i <= 5} {incr i} { puts "i*i=[expr $i*$i]" } ● foreach foreach i {pine apple orange grape tomato} { puts $i } ● break と continue # orange 以降は表示しない foreach i {pine apple orange grape tomato} { if {$i == "orange"} { break; } puts $i } # orange だけは表示しない foreach i {pine apple orange grape tomato} { if {$i == "orange"} { continue } puts $i } ● switch switch オプション 変数名 { パターン1 {パターン 1 の時の処理} パターン2 {パターン 2 の時の処理} ... default {デフォルト処理} } switch $time { 12 {put "lunch"} 10 - ;# 次のパターンで指定された処理を行う 15 {put "oyatu"} 19 {put "supper"} default {put "not eat! work!"} } ■スクリプトの実行 ● eval set a {set dv 10} ;# 変数 a にコマンド set dv 10 を代入 eval $a ;# a に設定されたコマンド set dv 10 を実行 ●source source inside.tcl ;# 現在の tclsh 内で inside.tcl を実行 ■手続き (Procedure) :戻り値をもっているので C の関数と同じ proc 手続き名 引数リスト 手続本体 proc ave2 {a b} {expr ($a + $b) / 2} ;# 平均を求める関数 ave2 set c [ave2 1 3] ;# ave2 を使う例 ※定義された手続きはスクリプトのどこからでも利用可能 →手続呼び出しに対してスコープが問題になることはない ●変数のスコープ 手続き内の変数,手続きの引数 →ローカルなスコープをもつ 手続きの外側の変数 →グローバルだが,手続き内で使用するには global コマンドを 使用して使用の宣言が必要 # 手続きの定義 proc localsub {} { global g1 ;# 大域変数 g1 の使用宣言 incr g1 ;# g1 をインクリメント } set g1 2 ;# g1 に 2 を代入 localsub ;# 手続を呼び出す→ g1 がインクリメントされる puts $g1 ●参照渡しの引数 proc コマンドの引数は通常,値渡し # 引数をアドレス渡しにする (upvar) proc reftest {a} { upvar $a exout ;# exout を a の参照とする set exout 6 ;# a を 6 にする } set out 5 reftest out ;# 参照渡しで out の値を変更 puts $out # 引数の省略 proc hello {} { ;# {} の中身を空にする puts "Hello World" } ●引数のデフォルト値 proc 手続き名 {変数名 {変数名 デフォルト値} ...} { 手続本体 } proc dec {in {num 1}} { ;# num のデフォルト値は 1 expr $in - $num } dec 16 3 ;# 普通に呼び出し dec 3 ;# 二番目の引数を省略→デフォルト値が使用される ●可変個の引数 特別な変数名 args を使う proc vararg args { foreach i $args { puts $i } } vararg a b c * 結果 a b c ●手続きの戻り値 通常は最後のコマンドの結果 return を使うと明示的に戻り値を指定 return 戻り値 ■文字列操作 ●パターンマッチング 1) glob スタイル(ワイルドカード方式) csh のファイルマッチング方式と同じ *, ?, [chars], \* 2) 正規表現スタイル ., ^, $, \*, [chars], (exp), *, +, ?, exp1|exp2 ● regexp パターンにマッチしたら 1 を返す regexp {[A-Z]+} "The quick brown fox..." ;# 1 を返す パターンにマッチした部分を変数にいれる regexp {[A-Z][a-z]+} "The quick brown fox..." out ; puts $out ;# The が表示される ● regsub 正規表現マッチによる文字列の置換 regsub {f.x} "The quick brown fox ..." dog out puts $out ;# "The quick bworn dog ..." が表示される ※regexp, regsub ともに正規表現の前にオプションが指定できる -nocase 大文字小文字を区別しない -all (regsubのみ)変更をすべてのパターンについて行う ● string サブコマンドによってさまざまな文字列操作を行う string compare str1 str2 ;# str1 と str2 を比較 string first str1 str2 ;# str2 中で最初にでてきた str1 の位置 string last str1 str2 ;# str2 中で最後にでてきた str1 の位置 string index str index ;# str の index 番目の文字 string length str ;# str の長さ string match pat str ;# str の glob スタイルのパターン検索 string range str first last ;# str の first 位置から last 位置まで string tolower str ;# 小文字化 string toupper str ;# 大文字化 string trim str chars ;# str の前後の chars をとる string trimleft str chars ;# str の前の chars をとる string trimright str chars ;# str の後ろの chars をとる ■日本語文字列 ●kanji サブコマンドによって処理内容を指定 kanji code ;# 文字列 str の漢字コードを判定 kanji conversion from to str;# 文字列 str の漢字コードを from ;# から to に変換 kanji defaultInputCode code ;# ファイル入力コードの既定値の設定 kanji defaultOutputCode code;# ファイル出力コードの既定値の設定 kanji inputCode id code ;# id で示されるファイルの入力コー ;# ドの設定 kanji internalCode code ;# 内部処理コードの設定 kanji lsearch L pat ;# 日本語版 lsearch kanji lsort L ;# 日本語版 lsort kanji split str c ;# 日本語版 split kanji string ot arg. ;# 日本語版 string ※短縮形 klsearch (=kanji lsearch) klsort (=kanji lsort) ksplit (=kanji split) kstring (=kanji string) ■入出力とファイル操作 ●ファイル入出力 set fd [open "infile" r] ;# ファイルを開く while { [eof fd] } { ;# EOF になるまで gets fd instr ;# fd から instr へ読み込む # 処理 puts $outputstr ;# 出力 } close fd ;# ファイルを閉じる open fileName access [persmision] ;# ファイルをモード acess で開く # access = r, r+, w, r+, a, a+ close channelId ;# ファイルを閉じる eof channelId ;# ファイルが EOF なら真 flush channelId ;# 出力バッファをフラッシュ read [-nonewline] channelId ;# ファイルからの読み込み read channelId numBytes ;# ファイルからの numBytes 読み込み seek channelId offset [origin] ;# ファイルシーク tell channleId ;# ファイルの現在の読み込み位置を返す gets channelId [varName] # channelId から varName へ文字列を読み込む puts [-nonewline] [channelId] string # channelId へ string を出力,-nonewline で改行をつけない ●書式つき入力 (format) C の printf() とほぼ同じ puts [format "%d + %d = %d" $a $b [expr $a + $b]] ●書式つき出力 (scan) C の sscanf() とほぼ同じ scan $str "%d %d" a b ;# a, b に値を読み込む ●ディレクトリ操作 cd [dirName] pwd ●ファイルの操作 (file) サブコマンドによって制御 file option name [arg arg ...] サブコマンド atime, attributes, copy, delete, dirname, executable, exists, extension, isdirectory, isfile, join, lstat, mkdir, mtime, nativename, owned, pathtype, readable, readlink, rename, rootname, size, split, stat, tail, type, volume, writable ■プロセス操作 ●サブプロセス起動 exec exec [switchws] argc [arg ...] set filename abc exec chmod +x $filename ●リダイレクト exec cat $filename | wc exec cat $filename |wc > out eval exec cp [glob *.exe] ~/backup ※ eval をうまく使おう set infd [open |date r] ;# date コマンドの出力を読む set otfd [open |wc w] ;# wc コマンドに出力を渡す ●プロセス ID サブプロセス ID は以下の方法で得られる 1) exec コマンドの戻り値 2) pid コマンド set infd [open |date r] pid $infd #; date コマンドの PID ■プロセスの終了とエラーハンドリング ●例外のトラップ (catch) catch {set b [expr 5 / $a]} errstr # 戻り値→エラーコード(成功なら 0) # errstr にエラー文字列 ●強制終了 (error) error "エラーが起きた" ●return によるエラーコードのハンドリング return -code CODE 戻り値 →上位で catch によるエラーの処理が可能 ■Tcl内部状態の取得 ●配列情報 (array) array option arrayName [arg arg ...] array anymore arrayName searchId ;#サーチ対象があれば真 array donesearch arrayName searchId ;#サーチを終了 array exists arrayName ;# arrayName が配列なら真 array get arrayName [pattern] ;# 名前が pattern にマッチ ;# する要素のリスト array names arrayName [pattern] ;# 名前が patten にマッチす ;# る要素名のリスト array nextelement arrayName searchId ;# 次のサーチ対象 array set arrayName list ;# リストに配列名を与える array size arrayName ;# 配列の要素数 array startsearch arrayName ;# 配列に対するサーチを開始 ;# し,その ID を返す ●内部状態 (info) info option [arg arg ...] info args procName ;# 手続き procName の引数を返す info body procName ;# 手続き procName の本体を返す info cmdcount ;# コマンドの総実行回数を返す info commands pattern ;# パターンに一致したコマンドの名前 info default procName arg varname ;# 手続 procName の既定値を設定 info exists varname ;# 変数 varname が存在していれば真 info globals [pattern] ;# pattern に一致したグローバル変数 ;# patten なし→グローバル変数リスト info level [num] ;# num レベルの手続と引数 ;# num なし→現在のレベル数 info library ;# ライブラリディレクトリパス info locals [pattern] ;# pattern に一致したローカル変数 ;# pattern なし→ローカル変数リスト info procs [pattern] ;# pattern に一致した手続きりすと ;# pattern なし→すべての手続リスト info script ;# スクリプト実行中なら真 info tclversion ;# Tcl のバージョン info vars [pattern] ;# pattern に一致した変数リスト ;# pattern なし→全変数リスト 他にもある... ●time time script [opt] ;# スクリプトを opt 回実行した時の時間 ■トレース (trace) trace variable 変数名 条件 コマンド 変数がある条件の時にコマンドを実行 条件 r 読みだし時,w 書き込み時,u 変数が unset された時 コマンド 普通の Tcl コマンド文字列 trace variable abc u {puts "abc = $abc"} ■コマンド実行履歴制御 (history) -----------------------------------------------------------------------