ファイル

Tclは、プラットフォームに依存しないファイル操作を提供しています。

一般的に、UNIX, Windows, Macintoshは、それぞれ異なるファイル名規則を持っています。
例えば、UNIXのファイル名のセパレータは、スラッシュ(/)ですが、Windowsは、バックスラッシュ(\)で、Macintoshは、コロン(:)です。
Tclは、ネーティブ形式とUNIX形式のネーミングコンベンションを許すことでプラットフォームに依存しないファイルのパス名を扱うことができます。
つまり、どのプラットフォームでもUNIXスタイルのスラッシュ区切りのファイルのパス名を使えるのです。

# Windowsネーティブ形式
C:\Program Files\Application\Foo

# Windows UNC形式
\\Host\Share\Foo

# UNIX形式
C:/Program Files/Application/Foo

実際には、WindowsとMacintoshは、ドライブ名という概念があるので考慮が必要です。

fileコマンド

fileコマンドは、ファイルのステータスをチェックする方法を提供します。

以下は、2つのファイルのタイムスタンプを比較する例です。

proc filechk {file1 file2} {
    set time1 [file mtime $file1]
    set time2 [file mtime $file2]
    if {$time1 > $time2} {
	return "$file1 の方が新しい"
    } elseif {$time1 < $time2} {
	return "$file2 の方が新しい"
    } else {
	return "両方同じ"
    }
}

filechk /bin/sh /bin/bash

fileコマンドには以下のオプションがあります。

fileコマンドのオプション
file atime name ?time?ファイルの最終アクセス時間を10進数で返す。timeを指定するとアクセス時間を設定できます。
Windows FATファイルシステムは未サポートです。
file attributes name
file attributes name ?option?
file attributes name ?option value option value...?
プラットフォーム依存のファイル属性をフラグで返す。optionとvalueを指定すると、ファイル属性を設定できます。
UNIXプラットフォームには、-group, -owner, -permissionsオプションがあります。
Windowsプラットフォームには、-archive, -hideen, -readonly, -shortnameオプションがあります。
Macintoshプラットフォームには、-creator, -hidden, -readonly, -typeオプションがあります。
file channels ?pattern?オープンされたファイルのチャネル名をリスト形式で返す。patternを指定するとパターンにマッチ名前だけを返す。
file copy ?-force? ?- -? source target
file copy ?-force? ?- -? source ?soruce...? targetDir
ファイルまたはディレクトリをコピーする。-forceオプションはエラーを無視して強制的にコピーする。
file delete ?-force? ?- -? pathname ?pathname?ファイルまたはディレクトリを削除する。-forceオプションは、ファイルまたはディレクトリが存在しなくても無視する。
file dirname nameファイルのパス名から最後の要素を除いた名前を返す。
file executable nameファイルがカレントユーザによって実行可能であれば1を返す。そうでなければ0を返す。
file exists nameファイルが存在していれば1を返す。そうでなければ0を返す。
file extension nameファイルの拡張子を返す。
file isdirectory nameパス名がデイレクトリであれば1を返す。そうでなければ0を返す。
file isfile nameパス名がファイルであれば1を返す。そうでなければ0を返す。
file join name ?name...?1つ以上のファイル名を結合したパス名を返す。
file lstat name varNameファイルのステータスを変数に返す。varNameは配列変数として扱います。
シンボリックリンクが未サポートのプラットフォームでは、下記のfile statと同じ結果になります。
file mkdir dir ?dir...?ディレクトリを作成する。
file mtime name ?time?ファイルの最終更新時間を10進数で返す。timeを指定すると更新時間を設定できます。
file nativename nameパス名をネーティブ形式のパス名に変換して返す。
file owned nameファイルがカレントユーザのオーナーであれば1を返す。そうでなければ0を返す。
file pathtype nameabsolute, relative, volumerelativeのいずれかを返す。absoluteは絶対パス、relativeは相対パス、volumerelativeは、ドライブ相対を意味する。
file readable nameファイルがカレントユーザによって読み込み可能であれば1を返す。そうでなければ0を返す。
file readlink nameファイルがシンボリックリンクの場合、実体のファイル名を返す。
シンボリックリンクをサポートしていないプラットフォームでは未サポートです。
file rename ?-force? ?- -? source target
file rename ?-force? ?- -? source ?soruce...? targetDir
ファイルまたはディレクトリの名前を変更する。-forceオプションはエラーを無視して強制的に変更する。
file rootname nameファイルの拡張子とドット( . )を除いたパス名を返す。
file size nameファイルのバイトサイズを返す。
file split nameパス名の要素を分割して返す。
file stat name varNameファイルのステータスを変数に返す。varNameは配列変数として扱います。
file tail name最後のディレクトリセパレータの後の文字列を返す。
file type nameファイルのタイプを返す。file, directory, characterSpecial, blockSpecial, fifo, link, socketのいずれか。
file volumeドライブ名の一覧を返す。
file writable nameファイルがカレントユーザによって書き込み可能であれば1を返す。そうでなければ0を返す。

入出力コマンド

以下は、ファイル入出力に関係するコマンドの一覧です。
openからcloseまでは、ファイル入出力の基本的なコマンドです。

open name ?access? ?permission?ファイルまたはパイプをオープンしてチャネルIDを返す。
puts ?-nonewline? ?channel? string文字列を書き込む
gets channel ?varname?1行を読み込む
read channel ?numBytes?バイト数分か全データを読み込む
read -nonewline channel ?numBytes?全データを読み込み最後の\nを捨てる
tell channel現在のシークオフセットを返す。
seek channel offset ?origin?オフセット分シークする。originは、start, current, endのいずれか。
eof channelファイルの終わり(EOF)かどうかチェックする
flush channel書き込みバッファをフラッシュする。
close channelファイルをクローズする。
fconfigure channel
fconfigure channel name
fconfigure channel name value ?name value...?
チャネルオプションの設定と参照をする。
fblock channel読み込みでブロックするかテストする。
fileevent channel readable ?script?
fileevent channel writable ?script?
チャネルが読み込みまたは書き込み可能になった時にスクリプトを実行する。
fcopy inchan outchan ?-size size? ?-command script?チャネル間でデータをコピーする。

ファイルを読み込んで表示する例です。

if [catch {open "./foo.txt" r} fd] {
    error "ファイルがオープンできません"
}
while {[gets $fd line] >= 0} {
    puts stdout $line
}

close $fd

コマンドの出力をパイプ使って読み込んで表示する例です。

if [catch {open "|/bin/ls -l" r} fd] {
    error "パイプがオープンできません"
}
while {[gets $fd line] >= 0} {
    puts stdout $line
}

close $fd

ファイル、パイプ、ソケットの入出力を非同期に行うことができます。
非同期に処理を行うことで、同時に他の処理ができます。
次の例は、パイプ入力中にStopボタンまたはESCキーが押されたら処理を中止します。

button .start -text Start -command start
button .stop -text Stop -command stop
text .text
grid .start .stop -sticky we
grid .text - -sticky wens

proc start {} {
    global fd
    if [catch {open "|ls -lR /" r} fd] {
	error "パイプがオープンできません"
    }
    .text delete 1.0 end
    fileevent $fd readable async
}

proc stop {} {
    global fd
    catch {close $fd}
    .text insert end ***Stoped***\n
    .text see end
}

proc async {} {
    global fd
    if [eof $fd] {
	close $fd
    } else {
	gets $fd line
	.text insert end $line\n
	update
	.text see end
    }
}

bind . <Escape> stop

ファイル入出力の際に改行コードとencodingを指定する例です。

# 日本語SJISコードのファイルを読み込んで日本語EUCコードのファイルを書き込む
set fd1 [open "foo.txt" r]
set fd2 [open "bar.txt" w]
fconfigure $fd1 -translation crlf -encoding shiftjis
fconfigure $fd2 -translation lf -encoding euc-jp
while {[gets $fd1 line] > 0} {
    puts $fd2 $line
}
close $fd1
close $fd2