Newsgroups: comp.lang.tcl,comp.answers,news.answers From: joe@morton.rain.com (Joe Moss) Subject: FAQ: comp.lang.tcl Tcl Language Usage Questions And Answers Message-ID:Summary: Answers to common questions regarding the use of the Tcl language and building the Tcl interpreter Keywords: FAQ Tcl TclX Sender: joe@morton.rain.com (Joe Moss) Supersedes: Reply-To: tcl-faq@morton.rain.com (Tcl Q&A FAQ Maintainer) Organization: Morton & Associates Date: Fri, 1 Sep 1995 06:24:39 GMT Expires: Fri, 6 Oct 1995 06:24:35 GMT Posted-By: auto-faq 3.1.1.2 Archive-name: tcl-faq/usage Posting-Frequency: monthly
この日本語訳に対するご意見・ご質問・誤りのご指摘などは,訳者まで. また,その他の詳細な情報については,tcl-faq-j/README.J を参照して下さい.
本 FAQ ファイルは,Tcl プログラミング言語に関する, 共通的によく聞かれる質問を扱うことを目的としています. このような質問が本文章で整理されることにより, comp.lang.tcl の同じ投稿の繰り返しが減り, より情報的になることを期待します. また,本文書や他の FAQ によって, より多くの人々が Tcl や Tcl ベースのアプリケーションを より便利に使えるようになることも期待します.
このFAQは, Joe Moss (joe@morton.rain.com) により管理され,毎月 comp.lang.tcl と news.answers に投稿され,Tclのアーカイブである ftp.aud.alcatel.com (198.64.191.10) の /tcl/docs ディレクトリに送られます.また,WWWでは http://route.psg.com/tcl.html で読むことが出来るでしょう.
本 FAQ は Tcl に関して扱うものであり, Tk についてのものではないことに注意して下さい. Tk ツールキットに関しては, Thomas J. Accardo (tja@cpu.com) により投稿されているFAQ (後述の質問: "Tclの情報をもっと知るには?" の中にある その他の Tcl FAQを参照のこと). しかしながら,必然的に情報がだぶる場合もあります. Tk ウィジェットの扱い方に関する最もよく聞かれる質問のいくつかは, Tcl インタプリタの動作自体に対する誤解に起因するものです. こういった問題は,今後もここで扱われます. また,Tcl の扱いの幾つかの例題では, Tkのウィジェットを実行する必要がある場合もあるでしょう.
Tk に依存しない拡張(拡張 Tcl や Expect など,ただし,TkX や expecTk, BLT は除く)の使い方に関する質問と回答もまた,ここで取り扱われます.
この文章のソースは,現在では HTML 形式で管理するようになりました. テキストバージョンに質問番号や質問目次, From や Subject 行を魔法の様に自動的に付加するために,2 つのスクリプト (もちろん,Tcl で書かれています)を通しています. WWW から参照できるバージョンは,変更がある度に更新されますが, ASCII テキストバージョンは月に1度,生成,投稿されるだけです.
最終変更: Sat Sep 16 00:59:21 PDT 1995
ニュースグループ comp.lang.tclは, Tcl言語とそれをベースとするパッケージに関して議論を行うために存在します. Tclに関する各種FAQドキュメントは, ftp.aud.alcatel.com のディレクトリ /tcl/docs から入手可能で,そこには出版されたものやオンライン上で入手可能な 各種情報とそのポインタが記載されています. これらは,World Wide Web でも, http://www.smartpages.com/bngfaqs/comp/lang/tcl/top.htmlからアクセス可能です.
>Webで情報サーフィンできるなら,以下の3つがスタート点としてよいでしょう.
http://www.sco.com/Technology/tcl/Tcl.html
http://web.cs.ualberta.ca/~wade/HyperTcl/
http://www.sunlabs.com/research/tcl/
また,本文書の末尾にある"外部URLリスト" から手繰る手もあります.
もちろん,配付品のソースコードそれ自体にも, 多くの重要なドキュメントが含まれています. 最新の正式バージョンは 7.4 で,早期α試験バージョンである 7.5a1版も入手可能です.これらは, ftp://ftp.smli.com/pub/tcl/ から得られます.
Tcl の配布物の中から"porting.notes" というファイル名のファイルを探して見ましょう. これには,これまで多くの人々から寄せられた, 様々なマシンや OS 上で Tcl を動かす際の注釈事項がまとめられています. また,コードに触ったりする前にまず最初に読むべき, "README"というファイルもあります (これは,どんなパッケージにも言えることですけど).
また, Sun Tcl/Tk Web pages から,移植問題関連のデータベースにアクセスできます.
さらに,本文章に後述する 様々なプラットホーム上でインタプリタを構築するには には,まだその殆どがTcl7.3以前のバージョンに関するものではありますが, そうした情報を提供しています.
TclはCコードと組み合わせて使用する事を想定されています. このために,これら2つを組み合わせる様々な方法が提供されています. 以下に,それらのうちの幾つかを示します (詳細はマン・ページを参照して下さい):
最後の2つは,その他のコマンドと同レベルの機能を提供するものではありません. が,(C側の)ソースコードをアクセスする事ができない場合には,必要となるでしょう.
これらの機能のより詳細を解説したドキュメントがあります.WWWで, http://psg.com/~joem/CmdWrite.htmlから読むことが出来る拡張Tclの配付に含まれる TclCommandWritingmanページを参照して下さい.
また, part two of Larry's Tcl FAQ や Tcl Bibliography には他の参考文献が掲載されています.
加えるに,標準Tclや他の拡張を組み込んだTclでは expectの 仮想ttyドライバ経由による制御,Xイベントやファイルイベント,タイマ, Tk4.x のアイドル・コールバックハンドラによる方法もあるでしょう. また, ftp://ftp.vnet.net/pub/users/drh/ から入手できるEmbedded Tkも見ておいた方が良いでしょう.
簡単な答えとしては,Tcl_AppInitを, 欲しい拡張の初期化プロシージャ全てを呼び出すように改造することです. これはTclソース配付品から,tclAppInit.c(Tkの場合は,tkAppInit.c)をコピーし, 変更した上で,使用しているTclライブラリに追加する事で行えます. 例えば,標準のTcl_AppInitは,以下の形式を取っています:
int Tcl_AppInit(interp) Tcl_Interp *interp; /* Interpreter for application. */ { /* ... */ if (Tcl_Init(interp) == TCL_ERROR) { return TCL_ERROR; } /* ... */ }「foo拡張」を追加するには, 次のようにfooの初期化関数の呼び出しを追加してやります:
int Tcl_AppInit(interp) Tcl_Interp *interp; /* Interpreter for application. */ { /* ... */ if (Tcl_Init(interp) == TCL_ERROR) { return TCL_ERROR; } if (Foo_Init(interp) == TCL_ERROR) { return TCL_ERROR; } /* ... */ }
より詳細な情報は,Tcl_AppInitのマンページや, tclAppInit.cファイル自体を参照すると良いでしょう.
(前述の)単純な回答はすべての場合に動作するものではない,というのが, より完全な回答です.多くの拡張は,より複雑な改造と, その拡張の使用に依存した組み込み方を必要とします.
拡張Tclを組み込みたい場合,Tcl_AppInitは, その配付に含まれるものを用いねばなりません. またプログラムをリンクする際には,標準Tclライブラリより前に拡張Tclの ライブラリをリンクしなければなりません.例えば:
cc -o mytclsh mytclXAppInit.c -ltclx -ltcl ...
あるいは,Tkを組み込んだインタプリタでは:
cc -o mywish mytkXAppInit.c -ltkx -ltk -ltclx -ltcl ...
幸いなことに, Make-A-Wishのような,複数の拡張を1つに組み込む為のパッケージもあります. また,他のポピュラーな拡張と一緒に組み込む為の設定ファイルを同時に配付して いる拡張もあります.例えば,Sven Delmas によって書かれた幾つかの拡張は, 指定された拡張を組み込んだインタプリタを構築するMakefileを生成するための, configureのオプションを指定することができます. これらの入手先は part four of Larry Virden's FAQ を参照のこと.
動的ローディングに関する議論は,しばしばcomp.lang.tclで見掛けられます. そして,さまざまな拡張の組み込みを 動的ローディングによって提供しようとするその願いは正しいものです. が,標準Tcl配付品では現在その機能はサポートされていません.
しかしながら,やがてリリースされる 7.5 版では組み込まれる予定であり, 現在入手可能の試験バージョンでは組み込み済であります.ですから,拡張を ロード可能モジュール形式に変換し始めることはできるでしょう.
Earle Lowe (lowee@cpsc.ucalgary.ca) は,次のように答えてます:
TCL/Tk が存在していてもいなくても関係なくどこででも動作できる スタンドアローンのプログラムを作りたいならば, もう少し作業する必要があります.
基本的には,これにはまず, TCL初期化ファイルをC文字列に変換してやることからはじめます. そして,Tcl_Init()やTk_Init()を呼ぶ代わりに, 変換された C 文字列を引数にTcl_Eval()を呼んでやります.
Alexei Rodriguez (alexei@cis.ufl.edu) が作成した wish_compiler パッケージを ftp://ftp.aud.alcatel.com/tcl/code/wish_compiler.shar.gz から入手すると良いでしょう.
このパッケージにはtcl2cコンバータが入っていて, その使用法の説明もついて来ます.
Makefile のちょっとした技を使えば, TCL/Tk の目的通りの使用 (様々な種類の wish を用いた,インタプリタ型言語としての使用)法で用い, コードを実行させる時には, コンパイルされたスタンドアローンプログラムを生成する様にすることも できるでしょう.
他の選択肢として,「組み込みTk」を利用することも可能です. これは, ftp://ftp.vnet.net/pub/users/drh/ から得られます.
幾つかの技が知られていますが,もっとも一般的なのは, Tclではコマンド行も含めてバックスラッシュ{\}が継続行の印として使える, という機能を利用したものです.例えば:
#! /usr/local/bin/tclsh puts "Hello World"
と書く替わりに,
#! /bin/sh # 次の行は/bin/shでは評価されるが, Tclではされない \ exec tclsh $0 ${1+"$@"} puts "Hello World"
と書いてやることで,実際にtclshが何処にインストールされているかに関係なく (ユーザの環境変数PATHにインストールされたパスが含まれている必要はありますが), 動作させることができます.
しばしば,ある言語拡張が(彼らにとっては必要不可欠であるにもかかわらず) なぜ,コア言語に統合されていないのかと聞かれます.
実に多くの人々が様々なシステムの上で, 非常に幅広い用途のためにTclを使用しているのだ,ということを考えに 入れねばなりません. また,Tclは元もと, プログラマがその作成するアプリケーションに必要なスクリプト言語をいちいち 作成する手間を削減するために設計された, 最低限のプログラミングの構成と枠組を提供する 組み込み用の言語であることも忘れてはなりません.
とは言うものの,コアTcl言語はここ数年で大きく発展して来ました. 追加された機能のうちの幾つかは, 他で開発された拡張から組み込まれたものです. 拡張によって提供される機能がTclの全てのユーザにとって嬉しいと思われた場合, JohnはコアTclにそれを組み込んで来ました. 例えば,連想リストやファイル入出力コマンド(と,ファイルハンドラ), unknownプロシージャは全て,標準Tclに組み込まれる以前は 拡張Tcl によって提供されていたものです. Tk 4.0 では,他の人々の手によってTcl7.3/Tk3.6用に開発された addinput や photo widget 拡張と同等の機能を含んでいます.
配列や,拡張Tclのキー付きリストを使うと良いでしょう.
例えば,以下のようにプログラムして:
keylset ttyFields ttyName tty1a keylset ttyFields baudRate 57600 keylset ttyFields parity strip
echo $ttyFields
を実行すると,以下を得ます:
{ttyName tty1a} {baudRate 57600} {parity strip}
あるいは配列を使って:
set ttyFields(ttyName) tty1a set ttyFields(baudRate) 57600 set ttyFields(parity) strip
拡張Tclには,システムの標準Cライブラリを使って乱数発生する
random
というコマンドがあります.
例えば,0から9までの乱数を生成するならば,以下のようにします:
set random_number [random 10]
また,srandom
というコマンドで乱数生成器の種を設定できます.
乱数の種を設定するには,以下のコマンドかその組合せを使用すると 良いでしょう.(ただし,Unix系のシステム):
[pid] [file atime /dev/kmem] [getclock] (拡張Tclのみ)
すべてTclで書かれた, 幾つかの仮想乱数生成関数がcomp.lang.tclに投稿されています. それらの一覧は, tcl-faq/part4 を参照して下さい.
以下はその1つで,拡張Tcl版と同じシンタックスでTclのみで書かれたものです. 定数はDon Libesによるものです.引数の正当性のちょっとしたチェックに 注目のこと.
proc random {args} { global RNG_seed set max 259200 set argcnt [llength $args] if { $argcnt < 1 || $argcnt > 2 } { error "wrong # args: random limit | seed ?seedval?" } if ![string compare [lindex $args 0] seed] { if { $argcnt == 2 } { set RNG_seed [lindex $args 1] } else { set RNG_seed [expr \ ([pid]+[file atime /dev/kmem])%$max] } return } if ![info exists RNG_seed] { set RNG_seed [expr ([pid]+[file atime /dev/kmem])%$max] } set RNG_seed [expr ($RNG_seed*7141+54773) % $max] return [expr int(double($RNG_seed)*[lindex $args 0]/$max)] }
y は複数の引数を要求し,x は複数の語を返すとします.
Tcl の eval
コマンドを使ってやります:
eval y [x]
もし可能であるならば,グローバル変数を使うよりも,
upvar
コマンドを試して見るべきです.
もし関数がイベント駆動であるならば,グローバル変数を使わざるを得ませんが.
# 配列の要素を出力する proc show_array arrayName { upvar $arrayName myArray foreach element [array names myArray] { puts stdout "${arrayName}($element) = $myArray($element)" } } set arval(0) zero set arval(1) one show_array arval
上に示したように,プロシージャから配列を返させるには, 配列名を引数として与えてやるだけです. 配列に対して行ったどのような変更操作も,親(呼出元)の配列に反映されます.
拡張 Tcl は,キーと値の組合せのリストである,キー付きリストと呼ばれる コンセプトを導入していて,ネットワーク経由などでも,値をルーチンに引き 渡すことが出来ます.
例えば,幾つかのファイルに対して grep でパターンマッチを行うならば, 以下の方法などが考えられます:
Karl Lehenbauer (karl@NeoSoft.com) が書くところによれば:
set files [glob /home/cole/stats/*] proc parseInfo { site } { global files # # 変数siteは予めlistboxで設定されている # set in [open [concat "|/usr/bin/grep $site $files"] r] while {[gets $in line]>-1} { puts stderr $line } catch {close $in} }
問題点: マッチした文字列が,ディレクトリ順に戻らない.
もしリターン・コードをチェックし,かつ,そのコマンドの出力を使いたいならば: Kevin B. Kenny (kennykb@dssv01.crd.ge.com) が書くところによれば:
if [catch {exec ls} data] { # execがエラーを起こすならば, $errorCodeに終了ステータスが入る } else { # execは成功 } # どんな場合でも,`data' には子プロセスの出力全てが格納される.
Karl Lehenbauer (karl@NeoSoft.com)
が errorCode を,文字列 "CHILDSTATUS
",
子プロセスのプロセスID
,
子プロセスの終了ステータスの
3 つの要素を持つリストとして加えた点に注意して下さい.
プロシージャ名を無名(空文字列)にリネームします.例えば:
rename procedureName ""
Tcl 7.x では,グローバル変数tcl_precisionを1〜17の範囲で
set
します.例えば:
% expr 4*atan(1) 3.14159 % set tcl_precision 0 can't set "tcl_precision": improper value for precision % set tcl_precision 3 3 % expr 4*atan(1) 3.14 % set tcl_precision 16 16 % expr 4*atan(1) 3.141592653589793 % set tcl_precision 18 can't set "tcl_precision": improper value for precision
Tcl 6.xでは,tclExpr.cモジュール中で%g
の代わりに
%lf
を使うように書換えます.
従って,unknownプロシージャを書き換えれば, 独自の機能拡張を行えます. 望むならば,自動読み込みの機能を削除することさえも可能です.
例えば,次のように行います.
set olddisplay $env(DISPLAY) set env(DISPLAY) unix:0
回答を与えてくれた Joel Fine (joel@cs.berkeley.edu) に感謝.
環境変数が存在しているかどうかを調べるのは,次のようにします:
if [info exists env(VARNAME)] { # 環境変数が定義されているので,それを使う. set value $env(VARNAME) } else { # 環境変数が設定されていないのでデフォルト値を使う. set value "the default value" }
Tclでは、頭に0がついた数字は8進数(あるいは16進数. ただし,0の直後に文字'x'がついた場合)として評価されます. 時としてこれは便利な機能ですが,いくつかの問題点もあります.
Tcl 7.4b3以前では,インタプリタは,08のような数を浮動小数点数として 評価しようとしました.7.4b3からは,そのような数が与えられると インタプリタはエラーメッセージを生成するようになりました.
0を取り去らねばなりません.以下は考え得るstripzeros 機能の実装2種です:
proc stripzeros {value} { regsub ^0+(.+) $value \\1 retval return $retval }
あるいは,
proc stripzeros {value} { set retval [string trimleft $value 0] if { ![string length $retval] } { return 0 } return $retval }
そして,コマンドに値を整数値として渡したくて, かつ,その数値の頭に0が付き得る場合には,このプロシージャを まず通すようにします.例えば:
set dayofweek [expr $days%7]
とする代わりに:
set dayofweek [expr [stripzeros $days]%7]
プログラム名はグローバル変数 argv0
に代入され,その引数はグローバル変数 argv
に
リスト形式で格納されます.変数 argc
には,
argv
のリストの要素数が
納められます.例を示すならば:
#! /usr/local/bin/tclsh if { $argc != 2 } { puts stderr "$argv0: Usage: $argv0 <infile> <outfile>" exit 1 } set infile [lindex $argv 0] set outfile [lindex $argv 1]
時々,彼らが期待したのと違う (そして,他の言語が同じ状況で振舞う振舞いとも異なる) Tclの振舞を見付ける人がいます.彼らは,この期待外れの振舞いをバグと考えます. こうした状況のもっとも起こり得る場所は,コメント文と思われます.
Tclでは,パーザに通される全ての物は,コメントも含めて, 適切なリスト構造を取っていなければなりません(そして,そう,コメントはパーザを 通るのです.これは,幾つかの言語では単にその前の段階で削除してしまうだけ であるのと対象的です). 一般的にこれは,コメントアウトされた行中の括弧の対応がきちんと取れていなければ ならないことを意味します.
従って,例えばある条件をチェックするif
文があったとして,
別の条件をテストさせたいと思ったとしましょう.前の条件をコメントアウトし,
新しいif
文を書いたとします.
このコードは,おそらくは「対応する閉じ括弧がない」というエラーになるでしょう.
## 間違った例 if { $newflag } { # if { $oldflag } { puts hello }
この場合,括弧の対応を取るようにしなければなりません.例えば:
## 正しい例 if { $newflag } { # if { $oldflag } { puts hello # } }
Tclのコメントでこの他に面白いのは,継続行機構が適用される点でしょう. すなわち:
# これは末尾がバックスラッシュで終るコメント行 \ そして,この行も上のコメントの一部となる.
内部的には,Tclインタプリタはほぼ全ての物をnull末端の文字列として 蓄えています.これはバイナリデータな文字列(より的確に言うならば, nullが途中に組み込まれた文字列)として直接には扱えないことを意味します.
しかしながら,幾つかの操作はデータがTcl変数に格納されていなくとも 動作させることができます.例えば,ファイルハンドルは外部プログラムに 直接張り付けられます.
set infp [open "|compress -dc $fileName"] exec gzip -c $newFileName <@ $infp
あるいは,拡張Tclならば:
set infp [open "|compress -dc $fileName"] set outfp [open "|gzip -c $newFileName" w] copyfile $infp $outfp
あるいは,何らかの方法でTclで利用可能な形式にデータを変換してしまう 手もあるでしょう:
本節を寄贈してくれた Wayne Throop throop@aur.alcatel.com に感謝.
拡張Tclは,たくさんのこの手の機能を提供しています. 例えば,拡張Tcl には signal コマンドがあります.
signal
action siglist [command]ここでactionは, "
default
", "ignore
", "error
", "trap
", "get
"に加えて POSIXの"block
"と "unblock
"アクションのいずれか (もちろん,POSIX システムでのみ有効). siglistは,記号表現,あるいは数値表現のUnixシグナルのリスト (前置記法 SIG はオプション). commandは,定義するエラー・ハンドラ (あるいは,単に,{puts stdout "そんなキーは押さないで!"} :-). trap は期待通り動きますし,error と get は, キーボード・トラバーサルが必要な会話的プログラムでは,とても有効です.拡張 Tcl は,forkなどの機能も持っています.
回答は Brad Morrison (brad@NeoSoft.com).
インタプリタで評価される度毎に,
たかだか1レベルのみの置換だけが可能なのです.
また,変数置換の発生時には,インタプリタはドル記号を見つけると,それに続く
不正な文字に至るまで全ての文字列(ここで不正とは,英数字,及び下線記号以外の
文字として定義される)を変数名として扱います.従って,配列記法や
${varname}
形式では区切りとして認識されます.
$$var
の場合,
ドル記号の後の最初の文字が不正文字(次のドル記号)であるので,
変数名が存在しない事になり,変数置換は働きません
(2番目のドル記号はそのまま残ります).
そして残りのドル記号のための構文解析が開始され,その右側が変数名として
扱われます.このため,直ちに2番目のドル記号以降が変数置換され,
構文解析はそれ以降の置換がないかどうかを調べにいってしまいます.
それ以降何もないならば,インタプリタを通したこのパスによる置換は
終了します(1回のみ行われることに注意).
eval
コマンドはその引数をインタプリタに通します.
そこで,2回目のパスを
インタプリタに通すためにeval
を使う事ができ,
そうすれば$$var
を動かす事ができるでしょう:
% set a 5 5 % set var a a % puts $$var # 動かない $a % eval puts $$var # 動く.--- けど危険. 5
ですが,変数varの内容に特殊文字 (スペースやセミコロンなど)が含まれていると,問題が発生してしまうでしょう.
よりよい方法は,1引数のみ与えられた場合のset
コマンドの振舞を応用し,
コマンド置換を変数置換に組み合わせて使用するのです:
% puts [set $var] # 安全に動作. 5また,実際にはコマンド置換(一度に1組の
[]
にしか働かない)だけを
使ってもできます:
% puts [set [set var]] # 同じように動作 5
似たような問題として,変数var1, var2, と var3の値の出力を考えて見ましょう:
set var1 3.14159 set var2 hello set var3 13 foreach num {1 2 3} { puts "var$num = [set var$num]" }
は,以下を出力します:
var1 = 3.14159 var2 = hello var3 = 13
upvar
コマンドは異なった変数にも使えます.
加えるに,7.4版からは置換を行わせるのに使える
subst
コマンドが装備されています.
上記にあげた全ての方法は配列に対しても使えます.
この問題点を扱った長文の記事は, ftp://ftp.aud.alcatel.com/tcl/docs/README.programmer.gzで得られます.
ここに掲載するのは,短い答えです:
Q. 後になって実行するコマンドを組み立てようとしてるのですが, 空白や特殊文字を値に含む変数で問題が出てしまいます.
A. コマンドを組み立てる最も安全な方法は, list コマンドを使うことです. そうすれば,リスト構造を壊さずにすみます. 余計なエバリュエータによる評価が行われてしまうこともあるので, ダブルクォートの使用は避けましょう. 例題として, クリックしたら表示しているラベルを出力するボタンを生成するプログラムを 示します.
button $myname -text $label -command "puts stdout $label"
button $myname -text $label \ -command [list puts stdout $label]
A. これを行う最も明晰な方法は, 実行時の変数の使用を隠蔽するようにプロシージャを定義することです. そして,前に述べた様に listコマンドを用いてプロシージャの呼び出しを組み立てあげます. (実行中であってもプロシージャの定義は可能です. 別のプロシージャの内部で生成されても, それはグローバルなスコープを持ちます.)
button $myname -text $label -command \ [list puts stdout $ArrayOfDynamicStuff($label)]
button $myname -text $label -command \ [list puts stdout \$ArrayOfDynamicStuff($label)]
puts stdout {$ArrayOfDynamicStuff(foo)}配列要素の値に対する置換が働かなくなってしまいます.
button $myname -text $label -command \ "puts stdout \$ArrayOfDynamicStuff($label)"
proc doit { i } { global ArrayOfDynamicStuff puts stdout $ArrayOfDynamicStuff($i) } button $myname -text $label -command [list doit $label]
A. evalとダブルクォートを組み合わせて使おうとするのは, 止めましょう. なぜなら,そうすると,余計な評価が起動されてしまうからです. evalコマンドは,その引数が1つ以上の場合は,それらを1つに連結します. そのため,ダブルクォートで1つに括ってやる必要が,まったく無くなります. 前述のbuttonの例を拡張して見ましょう.
proc mybutton { myname label args } { button $myname -text $label \ -command [list puts stdout $label] $args }
proc mybutton { myname label args } { eval "button $myname -text $label \ -command [list puts stdout $label] $args" }
proc mybutton { myname label args } { set cmd {button $myname -text $label \ -command [list puts stdout $label]} eval $cmd $args }
A. ひょっとして,
wish: set foo bar wish: if {$foo == bar} {puts stdout bar} syntax error in expression "$foo == bar"の様に書いてはいないでしょうか.
これでは,文字列としても変数としても評価されません. 式中のオペランドとしての文字列は, ダブルクォートか中括弧で括ってやらねばなりません.
wish: if {$foo == "bar"} {puts stdout bar}や
wish: if {$foo == {bar}} {puts stdout bar}の様に書き換えましょう.
どちらを選ぶかは,展開させたいかどうかで決めて下さい.
この記述は,Jesper Blommaskog (d9jesper@dtek.chalmers.se)からのコントリビュートです.
install.index: (cd ${DESTDIR}/tclscripts/lib; \ echo 'source /usr/local/lib/tcl/init.tcl;\ auto_mkindex . *.tk' | tcl ; exit 0)
# local additions lappend auto_path /usr/local/lib/tcl_local \ $env(RDS_TCL_SCRIPTS)/libこのようにすれば, スクリプトはプロシージャをライブラリの中から参照しようとし, "unknown"コマンドが 自動的にプロシージャを読み込んでくれます.
linsert $list 0 ..と実行したのに,結果に .. が挿入されていないのかと聞かれます.
Jesper Blommaskog (d9jesper@dtek.chalmers.se) が, 以下のように答えています.
lappend以外のリスト操作では,戻り値を保存しなければなりません. これは,list,lindex,lrange,lreplaceに 当てはまります.
これは,ひょっとしたらやりたかった事が正しく動く例です.set list [ linsert $list 0 .. ]
From Frank Smith (frank@arraysystems.nstn.ns.ca) によると, 拡張 Tcl を持っているなら,
read $fileId [fstat $fileId size]のようにして実現可能だそうです. これは,fileIdからその時点で読み込める最大のバイト数を読み込み, 結果としてブロック化しません.
fflush()
を使ってパイプをフラッシュすることです.
もっとも,
しばしば,パイプラインの端のプログラムの制御が出来ない場合もあります.
その様な場合は,解決する唯一の解は,仮想端末(pty)を使う事です.
が,これはそう簡単に行えることではありません.
これは標準の Tcl ではサポートされていませんが, expectでは, spawnコマンドで ptyを開いてコマンドを起動する機能をサポートしています.
だれも答えてくれる人はいない様ですが,大丈夫. Tcl 自身が,そのバージョンを答えてくれます. 以下のように打ち込んで見ましょう.
info tclversionまた,Tk のバージョンは,以下のようにして知ることが出来ます.
puts $tk_versionその他の拡張では,他の手段があるでしょう(例えば拡張Tclでは,
infox version
でバージョン情報が得られます).
7.4版からは,tclshの実行ファイルはデフォルトで版番号をつけてインストール されるようになりました.これは,度のバージョンで走らせているのかを 分かりやすくしてくれます.しかしながら,こうした形でインストールされていない システムでは,バージョンをシェルプロンプト上で知るには以下のようにします:
echo 'puts $tcl_version;exit 0' | tclsh
(;exit 0
の部分はwishの特性からwishでのみ必要とされます.が,
これが必要でない場合でも何もしないから大丈夫です.)
Eric Bleeker (ericbl@paramount.nikhefk.nikhef.nl) が書くには:
ひょっとしたら,こんな風に書いていませんか:
% set foo "bar baz" bar baz % foreach aap $foo { set $aap(1) "something" } can't read "aap(1)": variable isn't arrayこれは,Tclが配列要素"aap(1)"を, 存在してもいないのに置換しようとしているのです. これを解決するには,以下のようにします.
% foreach aap $foo { set [set aap](1) "something" }この場合は,2 つの配列"bar"と "baz"は生成されます.
別の形式として,以下のような形も考えられます.
% foreach aap $foo { set ${aap}(1) "something" }
本章では幾つかのエラーとそのありうる(でも表面上は分からない)理由を 示します.
このエラーメッセージはTclからではなく,シェルから報告されるものです. スクリプトがおそらく #! で始まっていて,その後にTclインタプリタの パス名が記述されているのでしょうが,これが使用しているシステムにとって 長過ぎるのです.多くのUnixシステムでは,(#!を含めて)32文字にこの 長さは制限されています.従って,下記のような例はエラーとなるでしょう.
#! /usr/home/homedir/very/long/path/tclsh # rest of script
tclshの実行ファイルを別のディレクトリに移動するなり,シンボリックリ ンクを張るなりすれば,このパス長を短くすることができます.また,別解と してパスを明示的に指定しないという手も使えます. これを行う実例は,質問 「tclshがインストールされている場所に関係なく, スクリプトを動作させるには?」を参照のこと.
開き丸括弧で終る行をコメントアウトしてるのでしょう.詳細な情報は,質問 「コメントの扱いにバグを見付けたぞ!」 を参照してください.
丸括弧の対応が取れてないのでしょう.一見して分からないような状況に ある場合は, 「コメントの扱いにバグを見付けたぞ!」 を参照すると良いでしょう.
Tkは,sendコマンドを使うためには,安全なXサーバを使うことを要求し ます.サーバを安全にするには,Thomas AccardoのTk ツールキット使い方FAQ の質問2.A.7「Tk3.3をちゃんと起動するには - セキュリティエラーメッセー ジが出るのですが?」や, http://ce-toolkit.crd.ge.com/tkxauth/を参照すると良いでしょう.
このエラーは,何らかの算術演算操作を行っている際に,それが整数を要 求する演算であるにもかかわらず非整数を与えた場合に発生します.やっかい なことに,値が実は浮動点小数や単なるキャラクタ文字列であっても,それが 正当な整数に読めるならば,このエラーは発生しません.頭に0がつく数字の 扱い方は,質問頭に0が付いた数字を扱うには? を参照してください.
アプリケーションをコンパイル/リンクする際に,main
関数
が見付からないというエラーが発生する場合,おそらくは,アプリケーション
がTcl7.3以前用に書かれているにもかかわらず,それを7.4版以降にリンクし
ようとしているのでしょう.
Tclの昔のバージョンは main
関数をライブラリ
(libtcl.a
)に持っていました.これは,特にC++プログラムで,
さまざまな問題を引き起こすことになりました.そのため,Tcl7.4b1の開始時
にこれは削除されました.Tclライブラリをリンクするアプリケーションは,
現在ではmain
関数を自前で定義する必要があります.
本セクションは,Tcl配布品を様々なプラットホーム上でコンパイルするための 幾つかのヒントを掲載します.ここに掲載された内容は,最新のリリースでは 恐らくは当てはまらないでしょう.
Tcl 7.4以降のリリースをコンパイルするのに,標準配布品の構築で問題を 抱えて問い合わせをしようとする人々のために,Sunによって管理される オンラインデータベースが利用可能になりました. 自動的にコンパイルが終らない場合は, http://www.sunlabs.com/research/tcl/ の使用しているプラットホームに関する情報を覗いて見てください. 一方,まだ情報が登録されていないプラットホームにおいて インストールに成功したならば,その実験結果を是非とも公表してやってください.
Q.A2- 私のマシン上で Tcl を動かすために必要な情報を得るには?も参照のこと.
Dov Grobgeld (dov@menora.weizmann.ac.il)が, AIX 3.1.5で,TclとTkの共有ライブラリを作る方法を発表しています.:
Tclでは:
cc -o tkshar.o *.o -bE:tclshar.exp -bM:SRE -berok -lX11 -lm ar r libtclshr tclshar.oTkでは:
cc -o tkshar.o *.o -bE:tkshar.exp -bM:SRE -berok -Ltcl -lX11 -lm -ltclshr ar r libtkshr tkshar.oここで tckshar.exp と tkshar.exp は,外部関数のリストです.
AIX と IBM のフォントの問題もあります. X サーバを修正するパッチをIBMから入手する必要があり, そうすれば,フォント関連が動きます.
ポーティング ノート(ファイルporting.notes)を参照して下さい.
また,Configureが互換バージョンのopendir()
を
(コンパイルに)使うべきだと判断してしまうけど,
dirent.hが存在していないという問題の報告が幾つかなされています.
これは,glob
でエラーを引き起こします.
TclXのreaddir
でも同様です.
tcl 7.3では,opendir
の互換ファイルを
削除する変更を行いましょう.
そして,-DNO_DIRENT, -DUSE_DIRENT2を削除すれば,問題は解決します.
1993年5月4日に, John Kimball (jkimball@src.honeywell.com) から寄せられた情報によれば, 彼はTcl 6.7とTk 3.2をVMS 5.5 上にポーティングすることに成功したそうです. ファイルの情報はカタログを参照して下さい.
"#undef select"をtkEvent.cに加え, main.cの460行目あたりのTK_EXCEPTIONの参照を削除します.
Tkは,そのウィジェット立体表示のために, 表示枠のカラー確保のための独自の仕組みを使っています. これがTkを,"Pseudo Color" ディスプレイ・クラスで, 16セルのカラーマップのマシン上で実行する際に問題となります.
8bitプレーンを使えないならば, ディスプレイ・クラスを "StaticColor" にする様に, サーバを "-static"(Xsco) や "-analog"(Xsight)オプション指定で立ち上げるとよいでしょう. これは,カラーマップを完全に読み込みのみとし, 要求された色にできるだけ最も近い色を返すようにします.
この情報は, Keith Amann (Keith_Amann@stortek.com)から寄せられました.
この(scanf
/printf
の)問題は,
多くのシステムで見られます.これに関しては,あまり神経質になる必要はありません
--- `より高度な'機能が使えないだけです.
もしあなたがCのハッカーならば,同じ問題に出喰わす筈です.
例えば,(こうしたシステムでは)printf("%Ng", double_value)と strtod("+",&terminal) は,誤った答えを返すでしょう.
これは,4.0.1のオプティマイザのバグで, 4.0.2では修正されています. tclVar.cをオプション-O0(最適化無し)で コンパイルして下さい.
ポーティング・ノートの変更点に関する記述を参照して下さい.また,
Booker C. Bense (benseb@grumpy.sdsc.edu)は,
バージョン3.0.1.6ではキャラクタ型のポインタの深刻な問題があり,
Tclがクラッシュすると報告しています.
バージョン3.0.2.1を使うと多少はマシですが,
format
コマンドの些細なバグと,
scan
コマンドの深刻な問題は残るそうです
この問題に関しては,以前,大きな議論の元となりました.以前の記述を, この分野のオーソリティと思える方からの解説に置き換えることとします.
Robert Nicholson (robert@steffi.demon.co.uk)によれば:
これはTCL7.1 と TK3.4 を対象とします.
これは Thomas Funke の報告の改造です.
TCL7.1をNeXTにインストール
tcl7.1をNeXTSTEP 3.1にインストールするには,以下のようにします:
- configure を既定義のCPPで実行:
ボーンシェルを実行するために,"sh"と打ち込みます. そして,以下のようにタイプします.CPP='cc -E' ./configure- Makefile を編集:
tmpnam.oをCOMPAT_OBJSに追加:COMPAT_OBJS = getcwd.o waitpid.o strtod.o tmpnam.oこの時,NeXTライブラリlibsys_a.aで供給されるものに対して,
strtod
とtmpnam
を区別するために, 何か適当な名前に変更したいと思うでしょう. これを行うためには,AC_FLAGSに以下の行を付け加えることで出来ます.-Dstrtod=newstrtod -Dtmpnam=newtmpnam名称変更された関数の定義は,compat/*.cにあります.
全てが正しく動くかどうかをチェックするには, tclshを起動し,以下を実行します.
% expr {"0" == "+"} 0間違えた
strtod
関数を用いていたならば,返り値は1になり, 深刻なエラーとなります.精度に関するエラーは無視します.TclをNextにインストールするには?
Tk3.4 の中にも
strtod
関数の呼び出しがあるので, 前述の../tcl-7.1/compat/strtod.oにリンクを張り, AC_FLAGSに-Dstrtod=newstrtodを付け加える必要があります.注意: Tk の raise 試験は, ウィンドウマネージャにtvtwmを使っていると失敗します. この試験を通すためには,twmやfvwmなどに変更する必要があります.
Gordon Lack (gml4410@ggr.co.uk)によれば, SGIのCコンパイラには変数引数の扱いにいくつかのエラーがあるそうです.
tclVar.cは,IRIX C4.0.1では,変数に関するバグのため, 引数-O0でコンパイルされねばなりません.
Makefile のルール指定を以下のように書き換えてやります.
# GGR SG needs -O0 for varargs at 4.0.1 CC_SWITCHES0 = -O0 -I. -I${SRC_DIR} ${AC_FLAGS} ${MATH_FLAGS} \ ${GENERIC_FLAGS} ${PROTO_FLAGS} ${MEM_DEBUG_FLAGS} \ -DTCL_LIBRARY=\"${TCL_LIBRARY}\" tclVar.o: tclVar.c $(CC) -c $(CC_SWITCHES0) $<
Peter Neelin (neelin@pet.mni.mcgill.ca)は,次のような注意をしてます.
私は,ファイルConfig.mkを次のように変更して, TclX 7.3aをSGI(irix4.0.5)でコンパイルしました.
71c71 < CFLAGS=-cckr -D__GNU_LIBRARY__ --- > #CFLAGS= 106,107c106,107 < TCL_PLUS_BUILD=TCL_PLUS < CCPLUS=g++ --- > #TCL_PLUS_BUILD=TCL_PLUS > CCPLUS=CC 191,193c191 < MAN_DIR_SEPARATOR= < < LIBOBJS=strftime.o --- > #MAN_DIR_SEPARATOR=.
-D__GNU_LIBRARYが,srandom
問題を解決しますが,
思うに,-cckrがwaitpid
で
プロトタイプ・エラーを起こします(汚いですが,とりあえず動きます).
strftime
がなぜ必要だったかは,忘れてしまいました.
私の作成した Config.mkのバージョンが欲しいならば,メールして下さい.
Jeff Abramson (jra@hrcms.jazz.att.com)が語るには:
SunPro cc 2.0.1を使ってもgcc 2.5.8を使っても問題はありません. SunProならば,次のようにします.
CC=cc ./configure --prefix=directory_of_your_choice make CC=ccgccならば,以下の通りです.
CC="gcc -fwritable-strings" ./configure \ --prefix=directory_of_your_choice make CC="gcc -fwritable-strings"
Andrew Swan (aswan@soda.berkeley.edu)によって解明されました.
TclとTk双方で,非ANSIのSequent ccではなく,gccを用います.
Tclでは,数学ライブラリに,2つの問題があります.第1に,Sequentの数学ラ イブラリが,'
fmod
'関数を含んでいないことです.私は
fmod
のソースをftp.uu.netから入手し,サブディレクト リcompatに入れました.そして,それをMakefileに追加しました.fmod
は,'isnan
'と'finite
'関数を 内部で使用していますが,tclが無限とnanをサポートするとは信じなかったの で,それらはコメントアウトしただけです.数学ライブラリのもう1つの問題は,そのライブラリ中に'
tanh
'関数のコピーが2つ,含まれてしまっていることです. "ar
"を使ってライブラリをオブジェクトに分解し, 再度組み立て直して,tanh
のコピーを取り去ってしまえば,簡 単にfixできます.この様な変更を行えば,Tclのコンパイルはきれいに終了し,試験も,
scan
の試験以外はすべて通ります.どうも,Dynixでは,*scanf
関数が壊れてる様です.この問題は,8進数の終りで確認 できます.私はちょうど,この問題に取り掛かったところです.解決策は,*scanf
関数のソースを見付けだし,それを使うことになるでしょ う.Tkをコンパイルするために,私はwchar_tのようなものを定義した,新しい バージョンのstddef.hを作らねばなりませんでした.私はそれを別のインクルー ド・ディレクトリにコピーし,そのディレクトリを最初に指定して,それが参 照されるようにしました.Tkでも数学ライブラリの問題 (
tanh
) が発生します.これら以外もありますが,すべてのコンパイルはきれいに終了 し,すべての試験にもパスします.
AU/X 上でのソースのコンパイルが終ったならば, デフォルトのテーブルのアロケーションを拡張する -A {factor}引数をリンカに指定しなければなりません. {factor}は,デフォルトのアロケーションの何倍にするかを指定します. -A 2を試してみましょう.この移植に関するこれ以上の詳細は, Walter B. Kulecz (wkulecz@medics.jsc.nasa.gov)博士に 連絡すると良いでしょう.
関連するツールや移植の完了品,この話題に関するメーリングリスト情報 は,Larry VirdenのFAQ ( tcl-faq/part1) や ( tcl-faq/part4)を参照のこと.
Steve Furr (furr@qnx.com)が示す所によれば:
QNX 上で Tcl を動かすためにしたことをまとめるならば,