For Development HEAD DRAFTSearch (procedure/syntax/module):

12.34 file.util - ファイルシステムユーティリティ

Module: file.util

ファイルやディレクトリを扱う便利な手続き群を提供します。 これらの手続きはファイルシステムで述べられたプリミティブなシステム手続きの上に 構築されています。

このモジュール内の多くの手続きはfollow-link?というキーワード引数を取ります。 これは手続きがシンボリックリンクに出会ったときの動作を指定します。follow-link?が 真であれば、手続きはリンクの指す先のファイルに作用します。これがデフォルトの振舞いです。 follow-link?#fが渡された場合は手続きはリンクそのものに作用します。

名前つけ規則に関する注記:ファイルやディレクトリを 作成するのに"create"という語を使う処理系と"make"を 使う処理系があります。ファイルやディレクトリを削除するのにも"remove""delete"の流派があります。どちらも同じくらい広く使われているようなので、 Gaucheでは両方の名前を提供することにしました。


12.34.1 ディレクトリユーティリティ

Function: current-directory :optional new-directory

{file.util} 引数無しで呼ばれた場合、カレントディレクトリを返します。 文字列new-directoryが与えられた場合はプロセスのカレントディレクトリを new-directoryに変更します。変更が出来なかった場合はエラーとなります。

この関数はChezSchemeやMzSchemeなどいくつかのScheme処理系に見られます。

SRFI-170は引数を取らないcurrent-directoryを定義しています。 現在のディレクトリを返すのは一緒です。

Function: home-directory :optional user

{file.util} 名前または整数のユーザidで与えられたユーザuserのホームディレクトリを 返します。userが省略された場合はカレントユーザが使われます。 与えられたユーザが見付けられないか、ホームディレクトリを決定できなかった場合は #fが返されます。

Windowsネイティブ環境では、この関数はカレントユーザに対してのみ動作します。

Function: directory-list path :key children? add-path? filter filter-add-path?

{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")
Function: directory-list2 path :key children? add-path? filter follow-link?

{file.util} directory-listに似ていますが、ふたつの値を返します。最初の値は path内にあるサブディレクトリのリストで、次の値はそれ以外のエントリのリストです。 キーワード引数children?add-path?filterdirectory-listと同じ意味をもちます。

偽の値をfollow-link?に与えると、path内のシンボリックリンクを 辿りません;すなわち、path内にディレクトリへのシンボリックリンクがあった場合、 デフォルト、もしくはfollow-link?に真の値が与えられた場合は それは最初のリスト(サブディレクトリ)に入りますが、follow-link? に偽の値が与えられた場合は後者のリスト(その他のエントリ)に入ります。

Function: directory-fold path proc seed :key lister follow-link?

{file.util} ディレクトリ探索の最も基本的な手続きです。基本的な動作は以下に示すような再帰的なものです。

  • pathがディレクトリでない場合は(proc path seed) を 評価し、結果を返します。
  • pathがディレクトリであった場合、まず (lister path seed) を評価します。 手続きlisterは2つの値、パス名のリストと次のseedとなる値を 返さなければなりません。 続いて、directory-foldが各パス名に対して再帰的に呼ばれます。 各呼び出しの結果が次の再帰呼び出しのseedの値に使われます。

デフォルトのlisterdirectory-listを次のように呼び出すものです。

(lambda (path seed)
  (values (directory-list path :add-path? #t :children? #t)
          seed))

listerpath自身への参照 (".") やその親ディレクトリへの参照を 返してはなりません。また、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))))
Function: make-directory* name :optional perm
Function: create-directory* name :optional perm

{file.util} ディレクトリnameを作成します。nameに至るパスが存在しない 場合は必要なディレクトリが作成されます (Unixのmkdir -pコマンドと 同様です)。ディレクトリnameが既に存在していた場合は何もしません。 permは作成されるディレクトリのパーミッションビットを指定します。

Function: remove-directory* name :key if-does-not-exist
Function: delete-directory* name :key if-does-not-exist

{file.util} ディレクトリnameとその内容を再帰的に消去します (Unixのrm -rコマンドと同様です)。シンボリックリンクは辿られません。

キーワード引数if-does-not-existは、:error (デフォルト) か #f でなければなりません。 :errorの場合、nameが存在しなければエラーが投げられます。 #fの場合、nameが存在しなければ手続きは何もせずに返ります。

nameが存在し、ディレクトリでない場合はエラーが投げられます。 ディレクトリでもファイルでも削除したい場合はremove-filesが使えます。

Function: copy-directory* src dst :key if-exists backup-suffix safe keep-timestamp keep-mode follow-link?

{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を参照してください。

Function: create-directory-tree dir spec

{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を指すシンボリックリンクを作成します。

Function: check-directory-tree dir spec

{file.util} specで記述されるディレクトリ階層がdirの下に存在するかどうかを 調べ、存在すれば#t、そうでなければ#fを返します。

specの形式は上で説明したcreate-directory-treeと同じです。

specがオプションを含んでいる場合、該当するファイル/ディレクトリの 属性もそのオプションに合致するかどうかチェックされます。


12.34.2 パスネームユーティリティ

Function: build-path base-path component …

{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者はそれぞれ".""."".."と解釈されます。 componentsbase-pathの後に追加されます。 (sameupはMzSchemeから採られました)。

Function: absolute-path? path
Function: relative-path? path

{file.util} pathがそれぞれ絶対パスまたは相対パスならば#tを返します。

Function: expand-path path

{file.util} pathがチルダ表記を含んでいたらそれを展開したものを返します。 そうでなければpathそのものを返します。この手続きはpathが 存在しアクセス可能であるかどうかはチェックしません。

Function: resolve-path path

{file.util} pathexpand-pathと同様に展開し、 続いてpathの各コンポーネントに対してそれがシンボリックリンクであればリンク先の ものに置き換えてゆきます。pathが存在しないパスを指していたり、 シンボリックリンクの先が存在しなかったり、読み出せないディレクトリがあった場合は エラーとなります。

Function: simplify-path path

{file.util} pathから、親ディレクトリへの参照("..")と自分自身への参照(".")を 出来る限り取り除きます。この手続きはファイルシステムへはアクセスしません。

Function: decompose-path 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
Function: path-extension path
Function: path-sans-extension path

{file.util} それぞれ、pathの拡張子と、pathから拡張子を除いたものを返します。 pathが拡張子を持っていない場合はそれぞれ#fpathが返されます。

(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"
Function: path-swap-extension path newext

{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"
Function: find-file-in-paths name :key paths pred extensions

{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.exenotepad.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が返ります。

Function: null-device

{file.util} nullデバイス名を返します。cygwinを含むunixプラットフォームでは "/dev/null"、mingwを含むWindowsネイティブプラットフォームでは "NUL"が返されます。

Function: console-device

{file.util} コンソールデバイス名を返します。cygwinを含むunixプラットフォームでは "/dev/tty"、mingwを含むWindowsネイティブプラットフォームでは "CON"が返されます。

そのデバイスが実際に現在のプロセスから利用可能であるかどうかはチェックされません。


12.34.3 ファイル属性ユーティリティ

Function: file-type path :key follow-link?
Function: file-perm path :key follow-link?
Function: file-mode path :key follow-link?
Function: file-ino path :key follow-link?
Function: file-dev path :key follow-link?
Function: file-rdev path :key follow-link?
Function: file-uid path :key follow-link?
Function: file-gid path :key follow-link?
Function: file-size path :key follow-link?
Function: file-atime path :key follow-link?
Function: file-mtime path :key follow-link?
Function: file-ctime path :key follow-link?

{file.util} これらの手続きはpathで示されるファイルやディレクトリのアトリビュートを 返します。アトリビュート名は<sys-stat>のスロット名に対応しています。 ファイルの状態を参照して下さい。pathで示されるファイルが 存在しなければ#fが返されます。

pathがシンボリックリンクだった場合、オプショナルな引数 follow-link? に偽の値が与えられていない限り、これらの手続きは リンクの指す先のファイルに関する情報を返します。

MzSchemeとChickenにはfile-sizeがあります。 Chickenにはfile-modification-timeがあり、これはfile-mtimeと 同じです。

Function: file-is-readable? path
Function: file-is-writable? path
Function: file-is-executable? path

{file.util} pathが存在して、現在の実効ユーザがそれぞれ読み取り/書き込み/実行可能なら#tを 返します。 このAPIはSTkから取られました。

{file.util} pathが存在して、それがシンボリックリンクなら#tを返します。 (参照:ファイルの状態file-is-regular?, file-is-directory?).

Function: file-eq? path1 path2
Function: file-eqv? path1 path2
Function: file-equal? path1 path2

{file.util} path1path2で示されるファイルを比較します。 file-eq?file-eqv?path1path2が 全く同一のファイルを参照しているかどうか、すなわち、同じデバイス上にあり同じ inode番号を持つかどうかをチェックします。二つの手続きの違いは、 path1path2の最後のコンポーネントがシンボリックリンクで あった場合に、file-eq?はリンクそのものの比較をするが file-eqv?はリンクを辿った先のファイルの比較をする、という点です。

file-equal?path1path2をその内容まで考慮して比較します。 すなわち、二つのファイルがfile-eqv?の意味で同一でなかった場合、 file-equal?はファイルの内容を比較し、全てが一致した場合に#tを返します。

path1path2ともにディレクトリが与えられた場合の file-equal?の動作は未定義です。将来、ディレクトリ内容を スキャンするような拡張が加えられるかもしれません。

Generic Function: file-mtime=? f1 f2
Generic Function: file-mtime<? f1 f2
Generic Function: file-mtime<=? f1 f2
Generic Function: file-mtime>? f1 f2
Generic Function: file-mtime>=? f1 f2

{file.util} 二つのファイルの変更時間を比較します。それぞれの引数に対して、 次のような型のオブジェクトが渡せるようなメソッドが定義されています。

  • 文字列のパス名。そのパス名で示されるファイルから変更時間が取られます。
  • <sys-stat>オブジェクト (see ファイルの状態)。 stat構造体から変更時間が取られます。
  • <time>オブジェクト。その示す時間が変更時間と考えられます。
  • 数値。変更時間をUnix Epochからの秒数で表したものと見なされます。
;; "foo.c" より "foo.o" が新しいかどうか調べる
(file-mtime>? "foo.c" "foo.o")

;; "foo.log"が過去24時間以内に更新されたかどうかを調べる
(file-mtime>? "foo.c" (- (sys-time) 86400))
Generic Function: file-ctime=? f1 f2
Generic Function: file-atime=? f1 f2

{file.util} file-mtime=?と同じですが、ファイルの属性変更時間とアクセス時間に 関して比較します。 <, <=, >, >=を使う関数も同様に定義されています。


12.34.4 ファイル操作

Function: touch-file path :key (time #f) (type #f) (create #t)
Function: touch-files paths :key (time #f) (type #f) (create #t)

{file.util} pathもしくはリストpaths中の各パスの タイムスタンプを現在の時刻に更新します。 指定されたパスが存在しなかった場合、キーワード引数create#fでなければ、 その名前で大きさゼロのファイルが作成されます。

キーワード引数timeが与えられて#fでない場合、それは 非負の実数でなければなりません。現在の時刻のかわりにその値がタイムスタンプとして使われます。

キーワード引数type#f(デフォルト)か、シンボルatimeもしくは mtimeです。シンボルの場合は、それぞれアクセス時刻か変更時刻のみが更新されます。

註:touch-filesはファイルをひとつづつ処理するので、各ファイルの タイムスタンプが完全に同一にはならない可能性があります。

これらの手続きはシステムコールsys-utimeを使って作られています (ファイルの状態参照)。

Function: copy-file src dst :key if-exists backup-suffix safe keep-timestamp keep-mode follow-link?

{file.util} ファイルsrcdstへコピーします。コピー元ファイルsrcは 存在していなければなりません。コピー先ファイルdstが存在していた場合の ふるまいはキーワード引数if-existsによって以下のように指定されます。

:error

(デフォルト) dstが存在していたらエラーを通知する。

:supersede

dstsrcのコピーで置き換える。

: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セッティングによってマスクされた 値となります。

Function: move-file src dst :key if-exists backup-suffix

{file.util} ファイルsrcdstへ移動します。移動元ファイルsrcは 存在していなければなりません。移動先ファイルdstが存在した場合の ふるまいはキーワード引数if-existsによって以下のように指定されます。

:error

(デフォルト) dstが存在していたらエラーを通知する。

:supersede

dstsrcで置き換える。

:backup

dstの名前を変えてキープする。

#f

dstが存在していたら移動をせず#fを返す。

move-fileは移動が完了したら#tを返します。

if-exists:backupである場合、dstがリネームされる 名前はdstにキーワード引数backup-suffixで指定されるサフィックスを 付けたものとなります。デフォルト値は".orig"です。

ファイルsrcdstは別のファイルシステム上にあっても構いません。 その場合、move-fileはまずsrcdstと同じディレクトリの 一時ファイルにコピーし、それをdstにリネームし、それから srcを消去します。

Function: remove-file filename
Function: delete-file filename

[R7RS file] {file.util} 指定された名前のファイルを消去します。ファイルが存在しなかったり、ディレクトリであったり、 パーミッションがなく消去できなかった場合等にはエラーが報告されます。 delete-fileはR7RSで定義されています。

sys-unlinkと似ていますが、sys-unlinkはファイルが無かった場合に エラーをあげず#fを返すことに注意。(ディレクトリ操作参照。)

Function: remove-files paths
Function: delete-files paths

{file.util} リストpaths中の各パスを削除します。パスがファイルの場合は unlinkし、ディレクトリの場合はremove-directory*を 使って再帰的にその内容を消去します。存在しないパスは単に無視されます。

delete-filesremove-filesの別名です。

Function: file->string filename options …
Function: file->list reader filename options …
Function: file->string-list filename options …
Function: file->sexp-list filename options …

{file.util} ファイル filename から読み込むための便利手続き。 これらの手続きは、まず、指定された名前のファイルをオープンし、その オープンしたファイルに対してそれぞれ port->stringport->listport->string-list および port->sexp-list を呼びます(入力ユーティリティ手続き参照)。すべての内容が読み込まれる かまたは読み込み中にエラーシグナルがあがれば、ファイルはクローズされます。

これらの手続きはcall-with-input-fileと同じキーワード引数を取ります。 ファイルが見つからなかった場合の振舞いは キーワード引数:if-does-not-existによって指定できます。 それが:errorならエラーが報告され、 #fなら#fが返されます。

Function: string->file filename string options …
Function: list->file writer filename lis options …
Function: string-list->file filename lis options …
Function: sexp-list->file filenme lis options …

{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->filesexp-list->filelist->fileの特化したバージョンで、 string-list->fileはwriterとして(^[s p] (display s p) (newline p))を、 sexp-list->fileはwriterとして(^[s p] (write s p) (newline p))を 使います。


12.34.5 一時ファイルとディレクトリ

Parameter: temporary-directory

{file.util} 一時ファイルを作るのに適したディレクトリ名を保持しているパラメータです。 デフォルトの値はsys-tmpdirの戻り値です (パス名参照)。 sys-tmpdirとの違いは、これはパラメータなので アプリケーションが実行時に変更できることです。 ライブラリは柔軟性を高めるためにできるだけsys-tmpdirよりは こちらを利用するのが良いでしょう。

Function: call-with-temporary-file proc :key directory prefix

{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についてはディレクトリ操作参照。

Function: call-with-temporary-directory proc :key directory prefix

{file.util} 一意な名前を持つ一時ディレクトリを作成し、 その名前を引数としてprocを呼びます。 一時ディレクトリは、procから戻った場合でも、procがエラーを投げた場合でも、 消去されます。 procの返す値(複数可)がcall-with-temporary-directoryの戻り値となります。

一時ディレクトリはディレクトリdirectory以下に作られ、 その名前はprefixの後にいくつかの英数字を付け加えたものになります。 省略された場合、(temporary-directory)の値がdirectoryに、 "gtemp"prefixに使われます。

procに渡される名前はdirectoryとファイル名をつなげたパス名です。 絶対パスになるか相対パスになるかはdirectoryの値によります。

内部的に、この手続きはsys-mkdtempを呼んで一意なディレクトリを作っています。 sys-mkstempについてはディレクトリ操作参照。


12.34.6 ロックファイル

ファイルやディレクトリを作成する排他性は、しばしばプロセス間のロックに 使われます。以下の手続きはパッケージ化されたインタフェースを提供します。

Function: with-lock-file lock-name thunk :key type retry-interval retry-limit secondary-lock-name retry2-interval retry2-limit perms abandon-timeout

{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>コンディションが投げられます。

以下のキーワード引数が認識されます。

type

シンボルfiledirectoryのどちらか。

fileを指定した場合、open(2)O_EXCLフラグを使う 排他的ファイル作成を利用したロックファイルを使います。これがデフォルトの動作です。 ほとんどのプラッフォームで動作しますが、NFSの実装の一部に、 排他的ファイル作成のセマンティクスが正しく実装されていない場合があります。

directoryを指定した場合は、mkdir(2)の排他性を利用した ロックディレクトリを使います。これはどんなプラットフォームでも動作する はずですが、fileを指定した場合より遅いかもしれません。

retry-interval
retry-limit

時間を秒で指定する非負の実数を取ります。前者は主ロック獲得を再試行する 時間間隔、後者は再試行を繰り返す総時間を指定します。 デフォルトはそれぞれ、1秒と10秒です。再試行をしないようにするには、 retry-limitに0を渡してください。

secondary-lock-name

副ロックファイル(もしくはディレクトリ)の名前を指定します。 省略された場合は、lock-nameにサフィックス.2をつけたものが使われます。 副ロックファイルの名前は、同じ(主)ロックファイルを使うプログラム全てで 一致していなければなりません。 特に変える必要が無ければデフォルトのままにするのが良いでしょう。

retry2-interval
retry2-limit

retry-intervalretry-limitに似ていますが、 こちらは副ロックファイルの再試行間隔と最長再試行時間を指定します。 副ロックが衝突する確率は通常極めて低いので、これらのパラメータを 調整する必要は滅多に無いでしょう。デフォルトの値はそれぞれ 1秒と10秒です。

perms

ロックファイルもしくはディレクトリのパーミッションを、非負正確整数の ビットマスクで指定します。デフォルトは、ロックファイルに対して#o644、 ロックディレクトリに対しては#o755です。

ロックを獲得/解放したり盗んだりするには、ロックファイル自身のパーミッションではなく、 ロックファイルが置かれるディレクトリのパーミッションが関係することに注意してください。

abandon-timeout

時間の長さ(秒)を非負の実数で指定します。 with-lock-fileがロックファイルを見つけて、そのタイムスタンプが 現在の時刻マイナスこの時間よりも古いものだった場合、ロックを盗みます。 ロックを盗むことを禁止したければこの引数に#fを渡してください。 デフォルトは600秒です。

Condition type: <lock-file-failure>

{file.util} with-lock-fileがロックを獲得できなかった場合に投げられる コンディションです。<error>を継承します。

Instance Variable of <lock-file-failure>: lock-file-name

獲得しようとした主ロックファイルの名前です。

Gaucheは、OSによりサポートされるfcntlロックの機能も、 gauche.fcntlモジュールによって提供しています。 fcntlロックを使うべきかwith-lock-fileを使うべきかは、 アプリケーションによります。

fcntlロックの利点は次の通りです。

多くの場合、最も便利なのは最初の性質でしょう。プロセスが予想外に落ちてしまった 場合でも、ロックが残されてしまうことを心配しないで済みます。

けれども、fcntlロックには欠点もあります。

二番目の欠点のせいで、アプリケーション全体を知っていてコードを好きにできる場合でなければ、 fcntlロックを安全に使うことが非常に難しくなっています。 アプリケーションコードが、そのアプリケーションで使うためだけのファイルをロックするのに fcntlロックを使うのは何も問題ありません。 しかしライブラリ開発者は、ライブラリ内でfcntlロックを使うなら、 そのライブラリのユーザや他のライブラリが将来に渡って同じファイルを決してロックしない ということを保証しなければなりません(通常、そんなことは不可能です)。



For Development HEAD DRAFTSearch (procedure/syntax/module):
DRAFT