Guile色々/ディレクトリやディレクトリストリームの操作

変更履歴

概 要

目 次

参考資料

ディレクトリの操作

ディレクトリの作成

(mkdir path)
(mkdir path mode)

path が表すディレクトリを新たに作成し,mode によって指定されたアクセス権限を設定します.mode が省略された場合,それを #o0777 に設定してディレクトリを作成します.path はディレクトリ名を表す文字列,mode はアクセス権限を表す整数です(アクセス権限の変更を参照).返り値は unspecified です.

新たに作成されたディレクトリの実際のアクセス権限は,
mode & ~umask & #o0777
に設定されます. ここで,umask はディレクトリのアクセス権限を設定する際のマスクを表し,~umaskumask のビットごとの否定(反転)を表します.umask は,一般的には,#o0022 に設定されています. & はビットごとの論理積を表しています.#o0777 との論理積を取るということは, 初期的なアクセス権限は setuid,setgid,sticky bit を設定しないということを意味しています.

新たに作成されたディレクトリの所有者は,現行プロセス(mkdir手続きを実行したプロセス)の実効ユーザーに設定されます. 所有グループは,親ディレクトリに set-gid が設定されているかどうかによって変わります.set-gid が設定されている場合, 親ディレクトリの所有グループに設定されます. そうでない場合,現行プロセスの実効グループに設定されます.

親ディレクトリに set-gid が設定されている場合, 新たに作成されたディレクトリにも set-gid が設定されます.

▹(記録) この手続きの処理は, システムコールの mkdir(path,mode) によって行っています.

ディレクトリの削除

(rmdir path)

path が表すディレクトリを削除します. path はディレクトリ名を表す文字列です. 返り値は unspecified です.

削除されるディレクトリは空でなければいけません.

▹実行例:以下では /home/algo/tmp というディレクトリ上で作業しています.subdir というサブディレクトリを作成して,すぐに削除しています.さらに,元々存在していた scheme というサブディレクトリ(注:空でない)を削除しようとしてエラーが発生しています. またさらに,通常ファイル(guile-3.0.7.tar.gz)を rmdir 手続きを使って削除しようとしてエラーが発生しています.
$ guile
GNU Guile 3.0.5
      ...... 起動メッセージ ......
guile> (system "ls -l")
-rw-r--r-- 1 algo algo 21878396 12月 23 13:30 guile-3.0.7.tar.gz
drwxr-xr-x 2 algo algo     4096  2月  7 17:08 scheme
$1 = 0
guile> (mkdir "subdir")
guile> (system "ls -l")
-rw-r--r-- 1 algo algo 21878396 12月 23 13:30 guile-3.0.7.tar.gz
drwxr-xr-x 2 algo algo     4096  2月  7 17:08 scheme
drwxr-xr-x 2 algo algo     4096  2月 13 07:04 subdir
$2 = 0
guile> (rmdir "subdir")
guile> (system "ls -l")
-rw-r--r-- 1 algo algo 21878396 12月 23 13:30 guile-3.0.7.tar.gz
drwxr-xr-x 2 algo algo     4096  2月  7 17:08 scheme
$3 = 0
guile> (rmdir "scheme")
ice-9/boot-9.scm:1669:16: In procedure raise-exception:
In procedure rmdir: ディレクトリは空ではありません

Entering a new prompt.  Type `,bt' for a backtrace or `,q' to continue.
guile [1]> 
guile> (rmdir "guile-3.0.7.tar.gz")
ice-9/boot-9.scm:1669:16: In procedure raise-exception:
In procedure rmdir: ディレクトリではありません

Entering a new prompt.  Type `,bt' for a backtrace or `,q' to continue.
guile [1]> 

▹(記録) この手続きの処理は,システムコールの rmdir(path) によって行っています.

カレントディレクトリの取得と変更

(getcwd)

カレントディレクトリの絶対パスの名前(文字列)を返します.

▹(記録) getcwd 手続きの処理は,ライブラリ関数の getcwd(buf,size) によって行っています.buf は絶対パスを受け取るためのバッファ,size はバッファの大きさです.getcwd 手続きは,バッファの大きさが不足するには大きさを増やしながら絶対パスを取得しています.

(chdir path)

カレントディレクトリを path に変更します.path は変更先のディレクトリ(文字列)です.相対パスと絶対パスのどちらでも指定できます.返り値は unspecified です.

▹実行例
guile> (getcwd)
$1 = "/home/algo/guile-3.0.7/libguile"
guile> (chdir "../module")
guile> (getcwd)
$2 = "/home/algo/guile-3.0.7/module"
guile> (chdir "/home/algo/guile-3.0.7/module/ice-9")
guile> (getcwd)
$3 = "/home/algo/guile-3.0.7/module/ice-9"
guile> 

▹(記録) chdir 手続きの処理は, システムコールの chdir(path) によって行っています.

マスクの取得と変更

(umask)
(umask mode)

ディレクトリやファイルを作成する際のマスクを取得したり変更します. mode を省略したときには現在(現行プロセス)のマスク(整数)を返します.mode を指定したときには,マスクを mode に設定し,元々のマスク(整数)を返します.mode はマスクを表す整数です.一般的には8進数によって指定します.

▹実行例:以下では,現在のマスク(10進数の18;8進数の#o0022)を確認したあと, マスクを #o0045 (10進数の37)に変更してみています.
guile> (umask)
$1 = 18
guile> (umask #o0045)
$2 = 18
guile> (umask)
$3 = 37
guile> 

▹(記録) この手続きの処理は,システムコールの umask(mode) によって行っています.mode を省略した場合,mask=umask(0); umask(mask) によって現在のマスク(mask)を取得して(元のマスクに戻して)います.

ディレクトリやファイルの存在検査

(file-exists? path)

この手続きについては ファイルとリンクの操作[ファイルやディレクトリの存在検査] を参照して下さい.

ディレクトリストリームの操作

▹以下の手続き群に対して,Guileのマニュアルは次のように述べています.
Before using this and the procedures below, make sure to see the higher-level procedures for directory traversal that are available (see File Tree Walk).

▹ディレクトリストリームについて調べたことを ディレクトリストリームの調査 にまとめています.以下の説明は,その調べた結果を踏まえています.

▹ ディレクトリストリームとディレクトリファイルは一体化して処理されています. 両者は独立していません.ディレクトリストリームにはある程度の大きさのメモリブロックが割り当てられます.そのメモリブロックはすべてのディレクトリエントリーを格納するには小さいかも知れません.そのような場合, そのメモリブロックに入る量のディレクトリエントリーが,必要に応じて順々に,ディレクトリファイルから読み込まれます. そのため,ディレクトリストリームを処理している最中に,ディレクトリファイルのファイルオフセットを変更するような処理を行うことはできません.そんなことをすると,ディレクトリストリームに関する結果はオカシナものになるでしょう.

ディレクトリストリームのオープン

(opendir dirname)

dirname が表すディレクトリのディレクトリストリームをオープンして,ストリーム内のディレクトリエントリーの位置を示すオフセット(以下,ストリームオフセット)をストリームの先頭に設定して, そのストリームオブジェクトを返します. dirname はディレクトリ名を表す文字列です.

▹(記録) この手続きは, ライブラリ関数の opendir(dirname) を実行して, その返り値をGuileのオブジェクトに変換して返しています.

ディレクトリエントリーの取得

(readdir stream)

ストリームオフセットが示しているディレクトリエントリーを返すとともに,ストリームオフセットを次のディレクトリエントリーの位置に進めます.stream は opendir 手続きによって返されたディレクトリストリームです.返り値のディレクトリエントリーは, ファイルやディレクトリの名前(文字列)です.

ストリームオフセットがディレクトリストリームの末端に到達していた場合, EOF オブジェクトを返します. EOF オブジェクトか否かは eof-object? 手続きによって検査できます.

▹(記録) 内部的には readdir ライブラリ関数を実行しています.

ディレクトリストリームのリセット

(rewinddir stream)

ストリームオフセットをディレクトリストリームの先頭に設定し直します.stream は opendir 手続きによって返されたディレクトリストリームです. 返り値は unspecified です.

▹ストリームオフセットだけでなく,ディレクトリファイルのファイルオフセットもリセットします.

ディレクトリストリームのクローズ

(closedir stream)

ディレクトリストリーム stream をクローズします. 返り値は unspecified です.

ディレクトリストリームの型検査

(directory-stream? object)

object がディレクトリストリームか否かを検査します. ディレクトリストリームの場合には #t を返し, そうでない場合は #f を返します,object は任意のオブジェクトです.

簡単な具体例

▹以下のスクリプトは,ディレクトリ名をコマンドライン引数から受け取って, そのディレクトリエントリーの一覧を表示します.
#!/usr/bin/guile \
-e main -s
!#

;; disp-dir.scm

(define (main args)
  (define dirname (cadr args))
  (define dirstream (opendir dirname))
  (let loop ((entry (readdir dirstream)))
    (when (not (eof-object? entry))
      (display entry) (newline)
      (loop (readdir dirstream))))
  (closedir dirstream))
以下の実行例では,guile-3.0.7のソースコードを展開したディレクトリのmoduleサブディレクトリの一覧を表示しています.
$ ./disp-dir.scm /home/algo/guile-3.0.7/module
srfi
rnrs
scheme
rnrs.scm
scripts
statprof.scm
.
..
oop
language
texinfo
Makefile.am
system
texinfo.scm
web
sxml
Makefile.in
ice-9
$
上記の実行例が示すように,カレントディレクトリを表す "." や親ディレクトリを表す ".." もディレクトリストリームの中に含まれています.さらに,エントリーの並び順は,端的に言って,デタラメです.例えば,ls -1 コマンドによってディレクトリの内容を表示した下記の結果とは並び順がまったく違います.
$ ls -1 /home/algo/guile-3.0.7/module
Makefile.am
Makefile.in
ice-9/
language/
oop/
rnrs/
rnrs.scm
scheme/
scripts/
srfi/
statprof.scm
sxml/
system/
texinfo/
texinfo.scm
web/
(おしまい)