Tclコマンド編

 ファイル等の操作
 この章では、ファイル操作関連のコマンドを紹介します。
 これらのコマンドに共通する考え方として、チャネルIDを通してファイルのI/Oを行っています。チャネルIDは、インタプリタが自動で付けた名前(文字列)であり、ファイルへデータを書き込んだり、逆にファイルからデータを読み込む際に、それぞれのコマンドにそのチャネルIDを渡します。
 また、ここで「ファイル」としているものは、普通のファイルの他に標準入出力や、ソケット通信も含みます。
 それでは、既存のフラットファイル「in.txt」をオープンしてその内容を「out.txt」と言うファイルへ出力するスクリプトを説明しましょう。
 
proc proc_file1 {} {
  if { [catch {open in.txt} fp_i] == 1 } {
    puts "in.txt ファイルがありません"
    return
  }
  if { [catch {open out.txt w} fp_o] == 1 } {
    puts "out.txt ファイルをオープンできません"
    close $fp_i
    return
  }
  if { [catch {puts $fp_o [read $fp_i]}] == 1 } {
    puts "ファイルの入出力に失敗しました"
  }
  close $fp_i
  close $fp_o
  puts "成功しました"
}
proc_file1
プロシジャーの定義
入力ファイルのオープンとチェック
読み込みモードでオープン


出力ファイルのオープンとチェック
書き込みモードでオープン



読んで書き込む


チャネルを閉じる



実行する
 チャネルIDとしてインタプリタからは、例えば「file5」のような文字列が返されます。
これがソケットだと、「sock8」とかになりますが、あくまでインタプリタが、独自に返す文字列ですから、リテラルな文字列をコーディングしないようにしてください。
 次の例は、ソケット通信を行ったものです。
 自分のPCで実験する場合は、サーバーとクライアントのIPアドレスを両方とも自分のPCのIPアドレス(ex. 127.0.0.0)にすればできますが、場合によっては他人に迷惑が掛かりますので、自己責任においてのみ慎重にお願いします
(どんなことが起きても、ボクは責任持てませんよぉ〜。)
 

サーバー側のマシン:127.0.0.0

クライアント側のマシン:127.0.0.0

 
proc proc_test1 {sock client port} {
  puts "proc_test1 : $sock $client $port"
  fconfigure $sock -blocking 1
  puts $sock "受け取ったよ"
  flush $sock
  fileevent $sock readable "proc_test2 $sock $client $port"
}

proc proc_test2 {sock client port} {
  puts "proc_test2 : $sock $client $port"
  puts [gets $sock]
  puts $sock OKOKOK
  flush $sock
}
socket -server proc_test1 -myaddr 127.0.0.0 8080
ソケットがクライアント側からオープンされた場合
に起動されるプロシジャを定義します。




次にクライアント側からサーバーへ書き込みされ
た場合に起動されるプロシジャを定義します。



最後にサーバー側のソケットを作成します。
  sock56 サーバーのソケットIDが返されます。
  クライアント側のソケットをオープンします。
socket -myaddr 127.0.0.0 127.0.0.0 8080
  proc_test1 : sock68 127.0.0.0 1394 ソケットがつながりました。
  クライアントのソケットIDが返されます。 sock32
  起動成功メッセージをサーバーから取り出します。
gets sock32
  メッセージ 受け取ったよ
  サーバーに文字列を送ります。
flush コマンドで実際に送信されます。
puts sock32 "クライアントのマシンぢゃよ"
flush sock32
  proc_test2 : sock68 127.0.0.0 1394
クライアントのマシンぢゃよ
一旦コネクト成立後は、サーバー側のソケットで読み
込みイベント(readable)のイベントハンドラが動きます
  ちゃんとにサーバーに届いた?
gets sock32
  届きました。 OKOKOK
  クローズするまで何回でもOK。
puts sock32 "もう一回ぢゃ!"
flush sock32
  proc_test2 : sock68 127.0.0.0 1394
もう一回ぢゃ!
 
   
gets sock32
    OKOKOK
 後は、サーバー側でクローズ後にクライアント側でクローズすれば終了です。
これをもう少し発展させれば、サーバー側に処理を依頼することも出来ますし、実際にそのようなシステムも稼動しています。
  open ファイルまたは、コマンドパイプラインをオープンし、それらへのチャネルIDを返す。
open fileName
open fileName access
open fileName access permissions
fileName ファイル、シリアルポートまたは、コマンドパイプライン。
最初の文字が「|」で始まっていなければ、ファイルとみなす。
access アクセス方法。
fopen関数形式指定:
r
r+
w
w+
a
a+
POSIXフラグ形式指定:
RDONLY
WRONLY
RDWR
APPEND
CREAT
EXCL
NOCTTY
NONBLOCK
TRUNC
下記のいずれかひとつ。
読み込み・既存専用。デフォルト。
読み書き・既存専用。
書き込み専用。無ければ作成。書き込みは頭から。
読み書き。無ければ作成。書き込みは頭から。
書き込み・既存専用。書き込みは最後に追加。
読み書き・既存専用。書き込みは最後に追加。
下記の組み合わせ。RDONLY|WRONLY|RDWRは必須
読み込み専用。
書き込み専用。
読み書き専用。
書き込みをファイルの最後から。
無い場合には作成。
CREATと一緒に指定すると既存の場合エラー。
端末デバイスの場合、制御端末にしません。
オープン時にプロセスがブロックしない。
既存の場合、ファイルサイズを0に切り捨てる。
permissions ファイルが作成される場合に与えるパーミッション。デフォルトは、0666。
例) >open wk.txt
file6
>gets file6
wk.txtの一行目
読み込みモードでファイルをオープンする。
チャネルID。
読み込み等は、チャネルIDを用いる。
  read 指定されたチャネルIDからの読み込み。
read ?-nonewline? channelId
read channelId numBytes
?-nonewline? ファイルの最後の文字を切り捨てる。UNIX等では、改行文字が捨てられる。
channelId チャネルID。openやsocketコマンドで返された文字列。以下のデフォルトあり。
stdin  : 標準入力
stdout : 標準出力
stderr : 標準エラー
numBytes 読み込むバイト数。
例) >open wk.txt
file6
>read file6
wk.txtの一行目
二行目
三行目
wk.txtは、3行のテキストファイル。
チャネルIDは、「file6」
最後の改行文字まで読み込まれることに注意。
  close チャネルをクローズする。
close channelId
channelId チャネルID。openやsocketコマンドで返された文字列。
例) >set fp [open wk.txt w]
>puts $fp "row : 1"
>close $fp
>puts $fp "row : 2"
can not find channel named "file6"
書き込みでオープンする。
1行書き込む。
クローズする。
再度書き込む。
すでにクローズされているので、エラーとなる。
  gets チャネルから、1行読み込む。
gets channelId ?varName?
channelId チャネルID。openやsocketコマンドで返された文字列。
?varName? 読み込んだ文字列を格納する変数名。指定された場合、返値は読み込まれた文字数。
EOFを読み込んだ場合、-1を返す。
指定されない場合、返値は読み込まれた文字列。
この場合EOFを読み込んでも空文字しか返さない。双方とも改行文字は除外される。
例) >set fp [open wk.txt]
>while { [gets $fp line] != -1 } {
puts $line
}
row : 1
row : 2
wk.txtは、2行のテキストファイル。
EOFまで繰り返し読み込む。
メモリに余裕があれば、readコマンドのほうが高速。
  eof EOFチェックをする。EOFの場合、1を返す。それ以外では0を返す。
eof channelId
channelId チャネルID。openやsocketコマンドで返された文字列。
例) >set fp [open wk.txt]
>while { [eof $fp] != 1 } {
puts [gets $fp]
}
row : 1
row : 2
getsコマンドと併用した例。
余分な変数を使わなくて済む。
  puts チャネルへの書き込み。
puts ?-nonewline? ?channelId? string
?-nonewline? 文字列の最後に改行文字を付けない。
?channelId? チャネルID。openやsocketコマンドで返された文字列。デフォルトは、stdout。
string 書き込む文字列。
例) >set fp [open wk.txt w]
>set cnt 1
>while { $cnt <= 10 } {
puts $fp $cnt
}
書き込みでオープンする。

10行書き出す。
  file 各種ファイル操作を行う。
file option name ?arg arg ...?
option ファイルに対する操作オプション。
atime, attributes, copy, delete, exists, isdirectory, isfile, readable 等々。
name ファイル名。
?arg arg ...? 操作オプションごとの引数。
例) >file atime wkwk.txt
922719600
>clock format 922719600 -format %T
00:00:00
>clock format 922719600 -format %D
03/30/99
>file exists nonFile.txt
0
アクセス時間を確認する。
累積された秒数。
何時:何分:何秒にするにはclockコマンドを使用。

日付にも変換できる。

カレントディレクトリにnonFile.txtファイルは存在するか
存在しない。
  flush チャネルに対してバッファを出力する。
flush channelId
channelId チャネルID。openやsocketコマンドで返された文字列。
例) >proc file_test3 {} {
puts -nonewline "start ..."
flush stdout
after 3000
puts " end"
}
>file_test3
start .. .end
標準出力に対して改行無しの「start ...」文字列を表示
後に、3秒経ってから「end」文字列を表示する。
flush コマンドを入れないと、2つの文字列が一緒に
表示されてしまう。
  fblocked 最後の入力操作がすべての利用可能な入力を使い果 た したか調べる。(よくわかりません(^ ^;)
The fblocked command returns 1 if the most recent input operation on channelId returned less information than requested because all available input was exhausted.
fblocked channelId
channelId チャネルID。openやsocketコマンドで返された文字列。
例)   For example, if gets is invoked when there are
only three characters available for input and no end-of-line sequence,
gets returns an empty string and a subsequent
call to fblocked will return 1.
どなたか具体例を教えてください!m(_ _)m
  fconfigure チャネルに関するオプションの取得と設定をする。
fconfigure channelId
fconfigure channelId name
fconfigure channelId name value ?name value ...?
channelId チャネルID。openやsocketコマンドで返された文字列。
name オプションの名前。省略すると全オプションおよび値のリストを返す。
-blocking, -buffering, -buffersize, -encoding, -eofchar, -translation
value オプションに対する値。省略すると名前に対する値を返す。設定するとその値で置き換える。
例) >set fp [open euc.txt]
>read $fp
、ウ、j、マ」ナ」ユ」テ・ウ。シ・ノ、ヌ、ケ
kokoha ASCII
>close $fp
>set fp [open euc.txt]
>fconfigure $fp -encoding euc-jp
>read $fp
これはEUCコードです
kokoha ASCII
>close $fp
最初は、デフォルトのまま。

EUCコードは文字化けしてしまう。



つぎは、エンコードをeuc−jpにしてみる。

成功!
注)上記のEUCコードには制御コード等が含まれる可能性が高いの
で、コンソールのコピー&ペーストは危険ですよ!
  fcopy 一つのチャネルから他のチャネルへデータをコピーする。
fcopy inchan outchan ?-size size? ?-command callback?
inchan データ入力元チャネルID。
outchan データ出力先チャネルID。
?-size size? 指定されると、そのバイト数分コピーし、されないとすべてコピーしてそれぞれコピーしたバイト数を返します。
?-command callback? バックグラウンドで動作させます。処理が完了すると、callbackプロシジャが呼ばれます。
その際の引数は、コピーバイト数およびエラー文字列で、正常終了の場合は、後者はありません。
tclshで実行する際には、イベントループ作成のためにvwaitコマンドを直後に記述すること。
wishで実行するのであれば不要。
例) >set in [open wk1.txt]
>set out [open wk2.txt w]
>fcopy $in $out
234
>close $in
>close $out
入力ファイルを「wk1.txt」、出力ファイルを「wk2.txt」とする。
outchanは、書き込みで。
フォアグラウンドで実行させる。
コピーしたバイト数。改行を含む。
クローズは必要。
  fileevent チャネルに対する読み込み・書き込みイベントの処理を設定する。
fileevent channelId readable ?script?
fileevent channelId writable ?script?
channelId チャネルID。openやsocketコマンドで返された文字列。
readable キーワード。読み込みイベントを記述する場合に指定する。
writable キーワード。書き込みイベントを記述する場合に指定する。
?script? 読み込み・書き込みイベント発生時の処理。
例)   このページのソケット通信を参照してください。
普通にファイルをオープンして、fileeventを設定すると、常にイベント
が発生して、ループしてしまうので要注意。
  socket TCPネットワークチャネルをオープンする。(ソケット通信のためのサーバーおよびクライアントを作成する)
最初の形式はクライアント用、後者はサーバー用。
socket ?options? host port
socket -server command ?options? port
?options? 各種オプション。
-myaddr addr 自分のアドレス。ドメインまたは、IP形式で指定する。
-myport port 自分のポート番号。
-async クライアント専用。非同期でサーバーに接続する。
そのため、即時に制御は戻るが、接続の有無はgetsやfblocked等で
確認しなければならない。
-sockname サーバー専用。ソケットアドレス、ホスト名、ポート番号を返す。
-peername クライアント専用。コネクト・バインドアドレ ス、ホスト名、ポート番号を
返す。
host サーバーのアドレス。ドメインまたは、IP形式で指定する。
port サーバーのポート番号。
-server command サーバー側の指定。クラアイアント側からコネクションされた際に1回だけ呼ばれる処理を command
として指定する。ソケットがつながった後は、fileevent コマンドで readable イベントに対する処理を定義して
おくこと。
例)   このページのソケット通信を参照してください。
  tell チャネルの現在のアクセスポインタを10進数で返す。対応していないチャネルの場合は、−1が返される。
tell channelId
channelId チャネルID。openやsocketコマンドで返された文字列。
例) >open wkwk.txt
>tell file1015210
0
>read file1015210
abc

>tell file1015210
5
>close file1015210
ファイルをオープンする。
その直後は、0。

ファイルをEOFまで、読み込んだ後は、


ポインタが進んでいる。