file.util
- ファイルシステムユーティリティ ¶ファイルやディレクトリを扱う便利な手続き群を提供します。 これらの手続きはファイルシステムで述べられたプリミティブなシステム手続きの上に 構築されています。
このモジュール内の多くの手続きはfollow-link?というキーワード引数を取ります。
これは手続きがシンボリックリンクに出会ったときの動作を指定します。follow-link?が
真であれば、手続きはリンクの指す先のファイルに作用します。これがデフォルトの振舞いです。
follow-link?に#f
が渡された場合は手続きはリンクそのものに作用します。
名前つけ規則に関する注記:ファイルやディレクトリを
作成するのに"create"
という語を使う処理系と"make"
を
使う処理系があります。ファイルやディレクトリを削除するのにも"remove"
と
"delete"
の流派があります。どちらも同じくらい広く使われているようなので、
Gaucheでは両方の名前を提供することにしました。
• ディレクトリユーティリティ: | ||
• パスネームユーティリティ: | ||
• ファイル属性ユーティリティ: | ||
• ファイル操作: | ||
• 一時ファイルとディレクトリ: | ||
• ロックファイル: |
{file.util
}
引数無しで呼ばれた場合、カレントディレクトリを返します。
文字列new-directoryが与えられた場合はプロセスのカレントディレクトリを
new-directoryに変更します。変更が出来なかった場合はエラーとなります。
この関数はChezSchemeやMzSchemeなどいくつかのScheme処理系に見られます。
SRFI-170は引数を取らないcurrent-directory
を定義しています。
現在のディレクトリを返すのは一緒です。
{file.util
}
名前または整数のユーザidで与えられたユーザuserのホームディレクトリを
返します。userが省略された場合はカレントユーザが使われます。
与えられたユーザが見付けられないか、ホームディレクトリを決定できなかった場合は
#f
が返されます。
Windowsネイティブ環境では、この関数はカレントユーザに対してのみ動作します。
{file.util
}
ディレクトリpath中のエントリのリストを返します。
リストは文字列順にソートされます。
デフォルトではエントリのベースネーム(パスの最後のコンポーネント)のみが 返されますが、キーワード引数add-path?に真の値が与えられた時は pathが各エントリの前に追加されます。 children?に真の値が与えられた時は、カレントディレクトリと親ディレクトリが リストから除かれます。
filter引数は、もし与えられれば、一つの引数を取る 手続きでなければなりません。ディレクトリ中の各エントリを引数としてその手続きが呼ばれ、 真を返したエントリのみが結果に含まれます。 filterに与えられるエントリはデフォルトではベース名のみですが、 引数filter-add-path?が真ならばpathが前に追加された名前となります。
pathがディレクトリでない場合はエラーが報告されます。
(directory-list "test") ⇒ ("." ".." "test.scm" "test.scm~") (directory-list "test" :add-path? #t) ⇒ ("test/." "test/.." "test/test.scm" "test/test.scm~") (directory-list "test" :children? #t) ⇒ ("test.scm" "test.scm~") (directory-list "test" :children? #t :add-path? #t :filter (lambda (e) (not (string-suffix? "~" e)))) ⇒ ("test/test.scm")
{file.util
}
directory-list
に似ていますが、ふたつの値を返します。最初の値は
path内にあるサブディレクトリのリストで、次の値はそれ以外のエントリのリストです。
キーワード引数children?、add-path?
、filterは
directory-list
と同じ意味をもちます。
偽の値をfollow-link?に与えると、path内のシンボリックリンクを 辿りません;すなわち、path内にディレクトリへのシンボリックリンクがあった場合、 デフォルト、もしくはfollow-link?に真の値が与えられた場合は それは最初のリスト(サブディレクトリ)に入りますが、follow-link? に偽の値が与えられた場合は後者のリスト(その他のエントリ)に入ります。
{file.util
}
ディレクトリ探索の最も基本的な手続きです。基本的な動作は以下に示すような再帰的なものです。
(proc path seed)
を
評価し、結果を返します。
(lister path seed)
を評価します。
手続きlisterは2つの値、パス名のリストと次のseedとなる値を
返さなければなりません。
続いて、directory-fold
が各パス名に対して再帰的に呼ばれます。
各呼び出しの結果が次の再帰呼び出しのseedの値に使われます。
デフォルトのlisterはdirectory-list
を次のように呼び出すものです。
(lambda (path seed) (values (directory-list path :add-path? #t :children? #t) seed))
listerはpath自身への参照 ("."
) やその親ディレクトリへの参照を
返してはなりません。また、listerの戻り値は現在のディレクトリからアクセス可能な
パス名でなければなりません。例えばpathが"/usr/lib/foo"
であり、
そのディレクトリが"libfoo.a"
と"libfoo.so"
を含んでいた場合、
listerは'("/usr/lib/foo/libfoo.a" "/usr/lib/foo/libfoo.so")
のようなリストを返す必要があります。
キーワード引数follow-link?はディレクトリを指しているシンボリックリンクに対して listerを呼ぶかどうかを決定します。follow-link?が真(デフォルト値)である 場合はそのようなシンボリックリンクに対してもlisterが呼ばれます。 一方、follow-link?が偽であればシンボリックリンクに対してはprocが呼ばれます。
次の例は、与えられたpath以下からemacsのバックアップファイル ("~"で終る名を持つファイル) のリストを返します。
(use srfi.13) ;; for string-suffix? (directory-fold path (lambda (entry result) (if (string-suffix? "~" entry) (cons entry result) result)) '())
次の例は与えられたpath以下全てのファイルとディレクトリ名をリストにして 返します。lister引数を使ってディレクトリ名そのものを結果に 含めていることに注目して下さい。
(directory-fold path cons '() :lister (lambda (path seed) (values (directory-list path :add-path? #t :children? #t) (cons path seed))))
{file.util
}
ディレクトリnameを作成します。nameに至るパスが存在しない
場合は必要なディレクトリが作成されます (Unixのmkdir -p
コマンドと
同様です)。ディレクトリnameが既に存在していた場合は何もしません。
permは作成されるディレクトリのパーミッションビットを指定します。
{file.util
}
ディレクトリnameとその内容を再帰的に消去します
(Unixのrm -r
コマンドと同様です)。シンボリックリンクは辿られません。
キーワード引数if-does-not-existは、:error
(デフォルト) か
#f
でなければなりません。
:error
の場合、nameが存在しなければエラーが投げられます。
#f
の場合、nameが存在しなければ手続きは何もせずに返ります。
nameが存在し、ディレクトリでない場合はエラーが投げられます。
ディレクトリでもファイルでも削除したい場合はremove-files
が使えます。
{file.util
}
srcが通常のファイルであれば、copy-file
と同じように
その内容をdstにコピーします。しかしsrcがディレクトリの場合は、
再帰的にディレクトリを辿り、その全てをdstへとコピーします。
cp -r
コマンドに相当するものだと考えて良いでしょう。
srcがディレクトリの場合、デフォルトではその下にあるシンボリックリンクは
辿られず、リンクそのものがコピーされます。リンク先の内容をもコピーしたい
場合はfollow-link?キーワード引数に真の値を与えてください。
つまり、follow-link?キーワード引数のデフォルト値は#f
です。
(このデフォルト値はcopy-file
と逆であることに注意してください。
copy-file
ではfollow-link?はデフォルトで真であり、
リンクそのものをコピーしたい場合に明示的に#f
を与える必要があります。)
他のキーワード引数の意味はcopy-file
と同じです。
詳細はcopy-file
を参照してください。
{file.util
}
specで指定されるディレクトリツリーをdirの下に作成します。
特定のディレクトリ構造を一気にセットアップする際に便利です。
spec引数は次に示される構造をもつS式です。
<spec> : <name> ; 空のファイル | (<name> <option> ...) ; 空のファイル | (<name> <option> ... <string>) ; 固定内容のファイル | (<name> <option> ... <procedure>) ; 内容を生成するファイル | (<name> <option> ... (<spec> ...)) ; ディレクトリ <name> : 文字列かシンボル <option> ... : キーワードと値の交代リスト
specの最初と2番目の形式では、名前nameを持つ空のファイルが作られます。 3番目の形式では与えられた文字列がファイルの内容となります。
4番目の形式では、手続きがファイルのパス名を引数として呼び出され、 その手続きがcurrent output portに出力した内容がファイルの内容となります。 引数に渡されるパス名はdir引数からの相対パスです。 手続きが呼ばれる時、その親ディレクトリは既につくられています。
最後の形式は、名前nameを持つディレクトリを作成し、 その子供として再帰的に指定されたspecによるファイル/ディレクトリを作成します。
optionによって、作られるファイル/ディレクトリの属性を細かく指定できます。 今のところ、次のオプションが認識されます。
:mode mode
整数modeでパーミッションのモードビットを指定します。
:owner uid
:group gid
整数uid/gidで作成されるエントリのオーナー/グループを指定します。 作成されるエントリのオーナー/グループを変更するには、 呼び出すプロセスに特権が必要かもしれません。
:symlink path
ファイルを作成するspecでのみ有効なオプションで、 pathを指すシンボリックリンクを作成します。
{file.util
}
specで記述されるディレクトリ階層がdirの下に存在するかどうかを
調べ、存在すれば#t
、そうでなければ#f
を返します。
specの形式は上で説明したcreate-directory-tree
と同じです。
specがオプションを含んでいる場合、該当するファイル/ディレクトリの 属性もそのオプションに合致するかどうかチェックされます。
{file.util
}
パス名のコンポーネントcomponent …をbase-pathに追加します。
パスの区切り文字は、プラットフォームに合ったものが選択されます。
base-pathには、ディレクトリ名か、#f
、
あるいはシンボルcwd
もしくはcld
を渡すことができます。
#f
は"."
と同じです。
cwd
の場合は現在のディレクトリ(current working directory)
が使われます。cld
の場合は現在のファイルをロードしているディレクトリ
(current loading directory)が使われます。
(current-load-path
, Schemeファイルのロード参照)。
但し、コードがファイルからロードされてたので
ない場合(REPLから呼び出した場合など)は、現在のディレクトリが代わりに使われます。
現在のロードディレクトリはbuild-path
が呼ばれた時に調べられることに注意して下さい。
build-path
がロード終了後に呼ばれる場合は、それはそのソースが置かれた
ディレクトリではないかもしれません。
パス名のコンポーネントcomponentは、文字列、
#f
、あるいはシンボルsame
またはup
です。
後3者はそれぞれ"."
、"."
、".."
と解釈されます。
componentsはbase-pathの後に追加されます。
(same
とup
はMzSchemeから採られました)。
{file.util
}
pathがそれぞれ絶対パスまたは相対パスならば#t
を返します。
{file.util
}
pathがチルダ表記を含んでいたらそれを展開したものを返します。
そうでなければpathそのものを返します。この手続きはpathが
存在しアクセス可能であるかどうかはチェックしません。
{file.util
}
pathをexpand-path
と同様に展開し、
続いてpathの各コンポーネントに対してそれがシンボリックリンクであればリンク先の
ものに置き換えてゆきます。pathが存在しないパスを指していたり、
シンボリックリンクの先が存在しなかったり、読み出せないディレクトリがあった場合は
エラーとなります。
{file.util
}
pathから、親ディレクトリへの参照(".."
)と自分自身への参照("."
)を
出来る限り取り除きます。この手続きはファイルシステムへはアクセスしません。
{file.util
}
パス名pathのディレクトリ部、拡張子を除いたファイル名、
そして拡張子の3つの値を返します。パス名が拡張子を持たない場合、
最後の値は#f
になります。パス名がディレクトリセパレータで
終わっている場合は2番目と3番目の値が#f
になります。
(後置されたディレクトリセパレータに関するこの取扱いは、
sys-dirname
/sys-basename
と異なることに注意して下さい。
sys-dirname
等は後置されたディレクトリセパレータを無視するという
シェル等の慣習に従っています。)
(decompose-path "/foo/bar/baz.scm") ⇒ "/foo/bar", "baz", "scm" (decompose-path "/foo/bar/baz") ⇒ "/foo/bar", "baz", #f (decompose-path "baz.scm") ⇒ ".", "baz", "scm" (decompose-path "/baz.scm") ⇒ "/", "baz", "scm" ;; Boundary cases (decompose-path "/foo/bar/baz.") ⇒ "/foo/bar", "baz", "" (decompose-path "/foo/bar/.baz") ⇒ "/foo/bar", ".baz", #f (decompose-path "/foo/bar.baz/") ⇒ "/foo/bar.baz", #f, #f
{file.util
}
それぞれ、pathの拡張子と、pathから拡張子を除いたものを返します。
pathが拡張子を持っていない場合はそれぞれ#f
とpathが返されます。
(path-extension "/foo/bar.c") ⇒ "c" (path-sans-extension "/foo/bar.c") ⇒ "/foo/bar" (path-extension "/foo/bar") ⇒ #f (path-sans-extension "/foo/bar") ⇒ "/foo/bar"
{file.util
}
pathの拡張子がnewextに置換されたものが返されます。path
が
拡張子を持たない場合は、pathに "." とnewextが追加されます。
newextが#f
の場合は、pathの拡張子が除かれたものが
返されます。
(path-swap-extension "/foo/bar.c" "o") ⇒ "/foo/bar.o" (path-swap-extension "/foo/bar.c" "") ⇒ "/foo/bar." (path-swap-extension "/foo/bar.c" #f) ⇒ "/foo/bar" (path-swap-extension "/foo/bar" "o") ⇒ "/foo/bar.o" (path-swap-extension "/foo/bar" "") ⇒ "/foo/bar." (path-swap-extension "/foo/bar" #f) ⇒ "/foo/bar"
{file.util
}
名前nameを持ち、述語predを満たすファイルをパス名のリストpaths
から探します。見つかった場合はファイルの絶対パス名を、見つからなかった場合は
#f
を返します。
nameが絶対パス名で与えられた場合はそれが存在するかどうかと predを満たすかどうかのみがチェックされます。
pathsのデフォルト値は環境変数PATH
から取られます。また、
predのデフォルト値はfile-is-executable?
(ファイル属性ユーティリティ参照)です。すなわち、デフォルトでは
この手続きはコマンドサーチパスから実行可能ファイルを探すのに使えます。
(find-file-in-paths "ls")
⇒ "/bin/ls"
;; アプリケーション"myapp"のユーザプレファレンスファイルを探す例
(find-file-in-paths "userpref"
:paths `(,(expand-path "~/.myapp")
"/usr/local/share/myapp"
"/usr/share/myapp")
:pred file-is-readable?)
extensionsキーワード引数には、nameに追加して探すべき拡張子の リストを指定できます。例えば次の例では、notepadに加え notepad.exeとnotepad.comがPATHから探されます。 拡張子つきの名前が見つかった場合、返されるパスは拡張子を含んだものとなります。
(find-file-in-paths "notepad" :extensions '("exe" "com"))
各パスについて、nameおよび拡張子つきの名前が順にチェックされます。
すなわち、/bin/b.comと/usr/bin/b.exeがあって
pathsが("/bin" "/usr/bin")
である場合、
extensionsに("exe" "com")
を与えてbを探すと、
/bin/b.comが返ります。
{file.util
}
nullデバイス名を返します。cygwinを含むunixプラットフォームでは
"/dev/null"
、mingwを含むWindowsネイティブプラットフォームでは
"NUL"
が返されます。
{file.util
}
コンソールデバイス名を返します。cygwinを含むunixプラットフォームでは
"/dev/tty"
、mingwを含むWindowsネイティブプラットフォームでは
"CON"
が返されます。
そのデバイスが実際に現在のプロセスから利用可能であるかどうかはチェックされません。
{file.util
}
これらの手続きはpathで示されるファイルやディレクトリのアトリビュートを
返します。アトリビュート名は<sys-stat>
のスロット名に対応しています。
ファイルの状態を参照して下さい。pathで示されるファイルが
存在しなければ#f
が返されます。
pathがシンボリックリンクだった場合、オプショナルな引数 follow-link? に偽の値が与えられていない限り、これらの手続きは リンクの指す先のファイルに関する情報を返します。
MzSchemeとChickenにはfile-size
があります。
Chickenにはfile-modification-time
があり、これはfile-mtime
と
同じです。
{file.util
}
pathが存在して、現在の実効ユーザがそれぞれ読み取り/書き込み/実行可能なら#t
を
返します。
このAPIはSTkから取られました。
{file.util
}
pathが存在して、それがシンボリックリンクなら#t
を返します。
(参照:ファイルの状態のfile-is-regular?
, file-is-directory?
).
{file.util
}
path1とpath2で示されるファイルを比較します。
file-eq?
とfile-eqv?
はpath1とpath2が
全く同一のファイルを参照しているかどうか、すなわち、同じデバイス上にあり同じ
inode番号を持つかどうかをチェックします。二つの手続きの違いは、
path1
やpath2の最後のコンポーネントがシンボリックリンクで
あった場合に、file-eq?はリンクそのものの比較をするが
file-eqv?
はリンクを辿った先のファイルの比較をする、という点です。
file-equal?
はpath1とpath2をその内容まで考慮して比較します。
すなわち、二つのファイルがfile-eqv?
の意味で同一でなかった場合、
file-equal?
はファイルの内容を比較し、全てが一致した場合に#t
を返します。
path1とpath2ともにディレクトリが与えられた場合の
file-equal?
の動作は未定義です。将来、ディレクトリ内容を
スキャンするような拡張が加えられるかもしれません。
{file.util
}
二つのファイルの変更時間を比較します。それぞれの引数に対して、
次のような型のオブジェクトが渡せるようなメソッドが定義されています。
<sys-stat>
オブジェクト (see ファイルの状態)。
stat構造体から変更時間が取られます。
<time>
オブジェクト。その示す時間が変更時間と考えられます。
;; "foo.c" より "foo.o" が新しいかどうか調べる (file-mtime>? "foo.c" "foo.o") ;; "foo.log"が過去24時間以内に更新されたかどうかを調べる (file-mtime>? "foo.c" (- (sys-time) 86400))
{file.util
}
file-mtime=?
と同じですが、ファイルの属性変更時間とアクセス時間に
関して比較します。
<
, <=
, >
, >=
を使う関数も同様に定義されています。
{file.util
}
pathもしくはリストpaths中の各パスの
タイムスタンプを現在の時刻に更新します。
指定されたパスが存在しなかった場合、キーワード引数createが#f
でなければ、
その名前で大きさゼロのファイルが作成されます。
キーワード引数timeが与えられて#f
でない場合、それは
非負の実数でなければなりません。現在の時刻のかわりにその値がタイムスタンプとして使われます。
キーワード引数typeは#f
(デフォルト)か、シンボルatime
もしくは
mtime
です。シンボルの場合は、それぞれアクセス時刻か変更時刻のみが更新されます。
註:touch-files
はファイルをひとつづつ処理するので、各ファイルの
タイムスタンプが完全に同一にはならない可能性があります。
これらの手続きはシステムコールsys-utime
を使って作られています
(ファイルの状態参照)。
{file.util
}
ファイルsrcをdstへコピーします。コピー元ファイルsrcは
存在していなければなりません。コピー先ファイルdstが存在していた場合の
ふるまいはキーワード引数if-existsによって以下のように指定されます。
:error
(デフォルト) dstが存在していたらエラーを通知する。
:supersede
dstをsrcのコピーで置き換える。
:backup
dstの名前を変えてキープする。
:append
dstの末尾にsrcの内容を追加する。
#f
dstが存在していたらコピーをせず#f
を返す。
copy-file
はコピーが完了したら#t
を返します。
srcがシンボリックリンクであった場合、copy-file
は
デフォルトでリンクを辿ります。つまり、ファイルの実体がコピーされます。
srcが存在しないパスを指すシンボリックリンクであった場合は
エラーが通知されます。
キーワード引数follow-link?に#f
を与えることで、
copy-link
にシンボリックリンクそのものをコピーさせることも
できます。この場合、srcが存在しないパスを指すシンボリックリンクで
あっても構いません。
if-existsが:backup
である場合、
dstがリネームされる名前は
dstにキーワード引数backup-suffixで指定されるサフィックスを
付けたものとなります。デフォルト値は".orig"
です。
デフォルトではcopy-file
は直接dstにコピーを行いますが、
キーワード引数safeに真の値が与えられた場合は、dstと同じディレクトリ
内の一時ファイルにまずコピーし、それが完了した時点でdstへとリネームします。
(safeが真でかつif-existsが:append
であった場合は、
dstがあればまずその内容を一時ファイルにコピーし、そこにsrcの内容を
追加し、最後にそれをdstへリネームします。)
コピーが何らかの理由で中断された場合、ファイルシステムはコピー前の状態へと
「ロールバック」されます。
キーワード引数keep-timestampに真の値が与えられた場合は、
copy-file
はコピー後にコピー先のファイルのタイムスタンプを
コピー元のタイムスタンプに合わせます。
キーワード引数keep-modeに真の値が与えられた場合は、
コピー先のファイルのパーミッションビットはコピー元のそれに合わせられます。
keep-modeが偽の場合(デフォルト)は、コピー先が既に存在して
safe引数が偽の場合にコピー先のもとのパーミッションが保持され、
そうでなければ#o666
がumaskセッティングによってマスクされた
値となります。
{file.util
}
ファイルsrcをdstへ移動します。移動元ファイルsrcは
存在していなければなりません。移動先ファイルdstが存在した場合の
ふるまいはキーワード引数if-existsによって以下のように指定されます。
:error
(デフォルト) dstが存在していたらエラーを通知する。
:supersede
dstをsrc
で置き換える。
:backup
dstの名前を変えてキープする。
#f
dstが存在していたら移動をせず#f
を返す。
move-file
は移動が完了したら#t
を返します。
if-existsが:backup
である場合、dstがリネームされる
名前はdstにキーワード引数backup-suffixで指定されるサフィックスを
付けたものとなります。デフォルト値は".orig"
です。
ファイルsrcとdstは別のファイルシステム上にあっても構いません。
その場合、move-file
はまずsrcをdstと同じディレクトリの
一時ファイルにコピーし、それをdstにリネームし、それから
srcを消去します。
[R7RS file]
{file.util
}
指定された名前のファイルを消去します。ファイルが存在しなかったり、ディレクトリであったり、
パーミッションがなく消去できなかった場合等にはエラーが報告されます。
delete-file
はR7RSで定義されています。
sys-unlink
と似ていますが、sys-unlink
はファイルが無かった場合に
エラーをあげず#f
を返すことに注意。(ディレクトリ操作参照。)
{file.util
}
リストpaths中の各パスを削除します。パスがファイルの場合は
unlink
し、ディレクトリの場合はremove-directory*
を
使って再帰的にその内容を消去します。存在しないパスは単に無視されます。
delete-files
はremove-files
の別名です。
{file.util
}
ファイル filename から読み込むための便利手続き。
これらの手続きは、まず、指定された名前のファイルをオープンし、その
オープンしたファイルに対してそれぞれ port->string
、
port->list
、port->string-list
および port->sexp-list
を呼びます(入力ユーティリティ手続き参照)。すべての内容が読み込まれる
かまたは読み込み中にエラーシグナルがあがれば、ファイルはクローズされます。
これらの手続きはcall-with-input-file
と同じキーワード引数を取ります。
ファイルが見つからなかった場合の振舞いは
キーワード引数:if-does-not-exist
によって指定できます。
それが:error
ならエラーが報告され、
#f
なら#f
が返されます。
{file.util
}
file->string
等の逆を行う手続きです。
手軽にファイルに何かを書き出したい時に便利です。
註: string->file
等の名前からして、
書き出されるオブジェクトを第一引数にする方が慣習にあっているかもしれません。
けれども、実際にこれらの手続きが使われる場面では、書き出されるオブジェクトとして
リテラルデータが来る場合も多く、だとするとより短いファイル名を先にした方が
読み書きしやすいだろうと考え、filenameを先に持ってきてあります。
options …部分はそのままcall-with-output-file
に
渡されます。次の例はfoo.txtが存在していればそれにデータを書き足します。
(string->file "foo.txt" "New text to append\n" :if-exists :append)
list->file
が取るwriter引数は、リストlisからのひとつの要素と
出力ポートの二つの引数を取る手続きで、適切な形式で要素をポートに書き出します。
string-list->file
とsexp-list->file
は
list->file
の特化したバージョンで、
string-list->file
はwriterとして(^[s p] (display s p) (newline p))
を、
sexp-list->file
はwriterとして(^[s p] (write s p) (newline p))
を
使います。
{file.util
}
一時ファイルを作るのに適したディレクトリ名を保持しているパラメータです。
デフォルトの値はsys-tmpdir
の戻り値です (パス名参照)。
sys-tmpdir
との違いは、これはパラメータなので
アプリケーションが実行時に変更できることです。
ライブラリは柔軟性を高めるためにできるだけsys-tmpdir
よりは
こちらを利用するのが良いでしょう。
{file.util
}
一意な名前を持つ一時ファイルを作成し、出力用にオープンして、
その出力ポートと名前を引数としてprocを呼びます。
一時ファイルは、procから戻った場合でも、procがエラーを投げた場合でも、
消去されます。
procの返す値(複数可)がcall-with-temporary-file
の戻り値となります。
一時ファイルはディレクトリdirectory以下に作られ、
その名前はprefixの後にいくつかの英数字を付け加えたものになります。
省略された場合、(temporary-directory)
の値がdirectoryに、
"gtemp"
がprefixに使われます。
procに渡される名前はdirectoryとファイル名をつなげたパス名です。 絶対パスになるか相対パスになるかはdirectoryの値によります。
(call-with-temporary-file (^[_ name] name)
⇒ 例えば "/tmp/gtemp4dSpMh"
出力したファイルを取っておきたい場合は、procの中でリネームしてください。 その場合、最終的なファイルと同じディレクトリに一時ファイルを作るように、 directory引数を指定するのを忘れないように。 ファイルシステムをまたぐリネームはうまくいかない場合があります。 また、コードがWindows上でも使われる可能性がある場合は、 リネームする前に出力ポートを閉じてください。 Windowsではオープンされているファイルはリネームできません。
内部的に、この手続きはsys-mkstemp
を呼んで一意なファイルを作っています。
sys-mkstemp
についてはディレクトリ操作参照。
{file.util
}
一意な名前を持つ一時ディレクトリを作成し、
その名前を引数としてprocを呼びます。
一時ディレクトリは、procから戻った場合でも、procがエラーを投げた場合でも、
消去されます。
procの返す値(複数可)がcall-with-temporary-directory
の戻り値となります。
一時ディレクトリはディレクトリdirectory以下に作られ、
その名前はprefixの後にいくつかの英数字を付け加えたものになります。
省略された場合、(temporary-directory)
の値がdirectoryに、
"gtemp"
がprefixに使われます。
procに渡される名前はdirectoryとファイル名をつなげたパス名です。 絶対パスになるか相対パスになるかはdirectoryの値によります。
内部的に、この手続きはsys-mkdtemp
を呼んで一意なディレクトリを作っています。
sys-mkstemp
についてはディレクトリ操作参照。
ファイルやディレクトリを作成する排他性は、しばしばプロセス間のロックに 使われます。以下の手続きはパッケージ化されたインタフェースを提供します。
{file.util
}
lock-nameという名前を持つファイルもしくはディレクトリ (ここでは
ロックファイルと呼びます) を排他的に作成し、
thunkを実行します。thunkから戻ってくるか、エラーが投げられたら、
ロックファイルは削除されます。thunkが正常に戻ってきた場合、
その戻り値がwith-lock-file
の戻り値となります。
ロックファイルが既に存在していた場合、with-lock-file
はタイムアウトになるまで
少し待ってリトライすることを続けます。細かい動作はキーワード引数で指定できます。
with-lock-file
実行中に深刻なエラーによって、ロックファイルを消せずに
プロセスが終了してしまう可能性があります。そのため、
with-lock-file
は、ロックファイルのタイムスタンプが非常に古い場合には
ロックを盗むことを許しています。例えば、通常アプリケーションはたかだか数秒しか
ロックしないはずなのに、10分前のタイムスタンプを持つロックファイルを見つけたとしたら、
以前のプロセスがクリーンアップをせずに落ちてしまったことは十分考えられるでしょう。
この振る舞いも、キーワード引数で制御することができます。
内部的には、安全な「盗ロック」を実現するために、二つのロックファイルが使われています。
主ロックファイル(lock-nameで指定される名前を持つもの)の作成と削除の
操作がそれぞれ、副ロックファイル(secondary-lock-fileで指定される名前を
持つもの。指定が省略された場合はlock-nameにサフィックス.2
をつけたもの)
によって保護されます。
副ロックは、二つ以上のプロセスが主ロックファイルを同時に盗もうとした場合を保護します。
副ロックファイルによるロックはほぼ常に極めて短い期間に
限定されるため、事故により副ロックファイルが残されてしまう可能性は
主ロックファイルに比べ非常に小さいです。それでももし副ロックファイルが
残されてしまった場合は、with-lock-file
は単に諦めます。副ロックファイルまで
盗むことはしません。
with-lock-file
がタイムアウトまでにロックを獲得できなけば、
<lock-file-failure>
コンディションが投げられます。
以下のキーワード引数が認識されます。
シンボルfile
かdirectory
のどちらか。
file
を指定した場合、open(2)
のO_EXCL
フラグを使う
排他的ファイル作成を利用したロックファイルを使います。これがデフォルトの動作です。
ほとんどのプラッフォームで動作しますが、NFSの実装の一部に、
排他的ファイル作成のセマンティクスが正しく実装されていない場合があります。
directory
を指定した場合は、mkdir(2)
の排他性を利用した
ロックディレクトリを使います。これはどんなプラットフォームでも動作する
はずですが、file
を指定した場合より遅いかもしれません。
時間を秒で指定する非負の実数を取ります。前者は主ロック獲得を再試行する 時間間隔、後者は再試行を繰り返す総時間を指定します。 デフォルトはそれぞれ、1秒と10秒です。再試行をしないようにするには、 retry-limitに0を渡してください。
副ロックファイル(もしくはディレクトリ)の名前を指定します。
省略された場合は、lock-nameにサフィックス.2
をつけたものが使われます。
副ロックファイルの名前は、同じ(主)ロックファイルを使うプログラム全てで
一致していなければなりません。
特に変える必要が無ければデフォルトのままにするのが良いでしょう。
retry-intervalとretry-limitに似ていますが、 こちらは副ロックファイルの再試行間隔と最長再試行時間を指定します。 副ロックが衝突する確率は通常極めて低いので、これらのパラメータを 調整する必要は滅多に無いでしょう。デフォルトの値はそれぞれ 1秒と10秒です。
ロックファイルもしくはディレクトリのパーミッションを、非負正確整数の
ビットマスクで指定します。デフォルトは、ロックファイルに対して#o644
、
ロックディレクトリに対しては#o755
です。
ロックを獲得/解放したり盗んだりするには、ロックファイル自身のパーミッションではなく、 ロックファイルが置かれるディレクトリのパーミッションが関係することに注意してください。
時間の長さ(秒)を非負の実数で指定します。
with-lock-file
がロックファイルを見つけて、そのタイムスタンプが
現在の時刻マイナスこの時間よりも古いものだった場合、ロックを盗みます。
ロックを盗むことを禁止したければこの引数に#f
を渡してください。
デフォルトは600秒です。
{file.util
}
with-lock-file
がロックを獲得できなかった場合に投げられる
コンディションです。<error>
を継承します。
<lock-file-failure>
: lock-file-name ¶獲得しようとした主ロックファイルの名前です。
Gaucheは、OSによりサポートされるfcntl
ロックの機能も、
gauche.fcntl
モジュールによって提供しています。
fcntl
ロックを使うべきかwith-lock-file
を使うべきかは、
アプリケーションによります。
fcntl
ロックの利点は次の通りです。
多くの場合、最も便利なのは最初の性質でしょう。プロセスが予想外に落ちてしまった 場合でも、ロックが残されてしまうことを心配しないで済みます。
けれども、fcntl
ロックには欠点もあります。
二番目の欠点のせいで、アプリケーション全体を知っていてコードを好きにできる場合でなければ、
fcntl
ロックを安全に使うことが非常に難しくなっています。
アプリケーションコードが、そのアプリケーションで使うためだけのファイルをロックするのに
fcntl
ロックを使うのは何も問題ありません。
しかしライブラリ開発者は、ライブラリ内でfcntl
ロックを使うなら、
そのライブラリのユーザや他のライブラリが将来に渡って同じファイルを決してロックしない
ということを保証しなければなりません(通常、そんなことは不可能です)。