ファイルのコピー
(copy-file old file new file) ▹この手続きは,old file を new file にコピーします.old file と new file はファイル名を表す文字列です.この手続きの返り値は unspecified です. new file が存在しない場合は新たに作成されます. その所有者は現行プロセス(copy-file 手続きを実行したプロセス)の実効ユーザーに設定されます. 所有グループは,親ディレクトリにset-gidが設定されているかどうかによって変化します.set-gidが設定されている場合には親ディレクトリの所有グループに設定されます.そうでない場合には現行プロセスの実効グループに設定されます. なお,所有権についてはopenシステムコールのマニュアル(O_CREATフラグの項)を参照して下さい. アクセス権限は old file と同じ権限が設定されます. old file や new file がシンボリックリンクの場合, シンボリックリンクを辿った先のファイルの中身がコピーの対象になります. シンボリックリンクの値がコピーされることはありません. old file がディレクトリで new file が通常ファイルの場合,エラーは発生せずに,new file はいったんオープンされ,何も書き込まれずにクローズされます. その結果として,new file が存在していなかった場合,0バイトの新たなファイルが作成されます.new file が存在していた場合,元の内容は消去されます. new file がディレクトリの場合,エラーが発生します. ▹実行例:一般的な場合の動作は自明とも言えるので,やや特殊な場合の実行例を示します.以下では,dirold というディレクトリを new.txt というファイルにコピーしようとしています.コピーの前後でカレントディレクトリの内容を確認しています.$ guile GNU Guile 3.0.5 ...... 起動メッセージ ...... guile> (system "ls -l") drwxr-xr-x 2 algo algo 4096 2月 9 06:43 dirold $1 = 0 guile> (copy-file "dirold" "new.txt") guile> (system "ls -l") drwxr-xr-x 2 algo algo 4096 2月 9 06:43 dirold -rwxr-xr-x 1 algo algo 0 2月 9 06:43 new.txt $2 = 0 guile>これは本来ならばエラーにして欲しいような場合ですが, エラーは発生せず,new.txtはいったんオープンされ, 何も書き込まれずにクローズされます.そのため, コピー後に new.txt という0バイトのファイルが新たに作成されています. ▹実行例:以下の実行例では,old.txt というファイルを dirnew というディレクトリにコピーしようとしてエラーが発生しています.赤字がエラーメッセージです.
guile> (copy-file "old.txt" "dirnew") ice-9/boot-9.scm:1669:16: In procedure raise-exception: In procedure copy-file: ディレクトリです Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue. guile [1]>▹(記録) old fileがディレクトリでnew fileが通常ファイルの場合の上記の処理は不自然に感じます.ソースコード(filesys.c)を見ると,old fileからデータを読み込むために read システムコールを利用しています.read システムコールは引数にディレクトリが指定されると EISDIR というエラーを発行します.ところがcopy-file のソースコードはこのエラーをチェックすることなくnew file(とold file)をクローズして正常終了します.そのために上記のような現象が発生しています.
ファイルの削除
(delete-file file name) ▹この手続きは,file name が表すハードリンクをファイルシステムから削除します. そのリンクがファイルデータへの最後のハードリンクだったときには, ファイルデータも削除します.file name はファイル名を表す文字列です.この手続きの返り値は unspecified です. file name がシンボリックリンクだった場合, リンクを辿ることはなく,シンボリックリンク自体を削除します. ファイルデータには何も影響しません. file name がディレクトリだった場合,エラーが発生します. つまり,delete-file 手続きによってディレクトリを削除することはできません. ▹実行例:以下の実行例は,file.lnk というシンボリックリンクを削除しています. 削除する前後のカレントディレクトリの内容を確認しています. シンボリックは削除されていますが, リンク先のファイルは削除されていません.guile> (system "ls -l") 合計 4 lrwxrwxrwx 1 algo algo 8 2月 9 07:33 file.lnk -> file.txt -rw-r--r-- 1 algo algo 17 2月 9 07:32 file.txt $1 = 0 guile> (delete-file "file.lnk") guile> (system "ls -l") 合計 4 -rw-r--r-- 1 algo algo 17 2月 9 07:32 file.txt $2 = 0 guile>▹実行例:以下の実行例では,dir というディレクトリを削除しようとしてエラーが発生しています. 赤字がエラーメッセージです.delete-file 手続きによってディレクトリを削除することはできません.
guile> (delete-file "dir") ice-9/boot-9.scm:1669:16: In procedure raise-exception: In procedure delete-file: ディレクトリです Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue. guile [1]>▹(記録)この手続きの処理はシステムコールの unlink(file name) によって行っています. 諸々の詳細については unlink システムコールのマニュアルを参照して下さい.
ファイルの移動(リネーム)
(rename-file old path new path) ▹この手続きは,old name を new name に名前を変更します. つまり,old nameは削除され(注:例外あり),old name がリンクしていたファイルデータは,rename以後,new name がリンクすることになります.old name と new name はファイル名を表す文字列です.この手続きの返り値は unspecified です. old name や new name がシンボリックリンクだった場合,リンクを辿ることはなく,シンボリックリンク自体の名前を変更します.シンボリックリンクがリンクしているファイル名に影響を与えることはありません. old name がディレクトリの場合,new name が存在しないか,空ディレクトリ(エントリーが1つもないディレクトリデータ)を指していなければなりません.そうでない場合,エラーが発生します. ▹実行例: 以下の実行例は,old.lnk というシンボリックリンクを new.lnk に移動しています. この場合,old.lnk が new.lnk に変化していて, リンク先の old.txt は何も変化しません.guile> (system "ls -l") lrwxrwxrwx 1 algo algo 7 2月 9 12:14 old.lnk -> old.txt -rw-r--r-- 1 algo algo 17 2月 9 12:13 old.txt $1 = 0 guile> (rename-file "old.lnk" "new.lnk") guile> (system "ls -l") lrwxrwxrwx 1 algo algo 7 2月 9 12:14 new.lnk -> old.txt -rw-r--r-- 1 algo algo 17 2月 9 12:13 old.txt $2 = 0 guile>▹実行例:以下の実行例は,dir-old というディレクトリの名前を dir-new に変更しています.移動先の dir-new が存在していない場合を試しています. 変更する前後で,カレントディレクトリの内容を確認しています.
guile> (system "ls -l") drwxr-xr-x 2 algo algo 4096 2月 9 12:18 dir-old $1 = 0 guile> (rename-file "dir-old" "dir-new") guile> (system "ls -l") drwxr-xr-x 2 algo algo 4096 2月 9 12:18 dir-new $2 = 0 guile>▹実行例:以下の実行例は,移動先の dir-new がすでに存在していて, 空のディレクトリだった場合を試しています.こ場合,問題なく移動できます.
guile> (system "ls -l") drwxr-xr-x 2 algo algo 4096 2月 9 12:30 dir-new drwxr-xr-x 2 algo algo 4096 2月 9 12:18 dir-old $1 = 0 guile> (system "ls -l dir-new") $2 = 0 (注:dir-new は空ディレクトリ) guile> (rename-file "dir-old" "dir-new") guile> (system "ls -l") drwxr-xr-x 2 algo algo 4096 2月 9 12:18 dir-new $3 = 0 guile>▹実行例:以下の実行例は,移動先の dir-new がすでに存在していて, 空でない場合を試しています.この場合,エラーが発生します.
guile> (system "ls -l") drwxr-xr-x 2 algo algo 4096 2月 9 12:36 dir-new drwxr-xr-x 2 algo algo 4096 2月 9 12:18 dir-old $1 = 0 guile> (system "ls -l dir-new") -rw-r--r-- 1 algo algo 17 2月 9 12:35 new.txt (注:dir-newは空でない) $2 = 0 guile> (rename-file "dir-old" "dir-new") ice-9/boot-9.scm:1669:16: In procedure raise-exception: In procedure rename-file: ディレクトリは空ではありません Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue. guile [1]>▹(記録) この手続きの処理は, システムコールの rename(old name,new name) によって行っています.rename システムコールの動作は意外に複雑です. 例えば,old name と new name が同一のファイルデータへのハードリンクだった場合, 何もせずに正常終了し,old name は削除されません(注:これが上で述べた「例外」です).さらに,エラー条件がけっこうあります. 諸々の詳細については rename システムコールのマニュアルを参照して下さい. ▹実行例:以下では,移動元のファイル(old.txt)と移動先のファイル(new.txt)が同一のファイルデータにリンクしている場合に何が起こるかを試しています. 移動操作の前に,両方のファイルのinode番号($2と$3)を求めて, 同一のファイルデータにリンクしていることを確認しています. 移動操作(rename-file)を行っても何も変化しないことが分かります.
guile> (system "ls -l") -rw-r--r-- 2 algo algo 16 2月 10 06:12 new.txt -rw-r--r-- 2 algo algo 16 2月 10 06:12 old.txt $1 = 0 guile> (stat:ino (stat "old.txt")) $2 = 16916694 guile> (stat:ino (stat "new.txt")) $3 = 16916694 guile> (rename-file "old.txt" "new.txt") guile> (system "ls -l") -rw-r--r-- 2 algo algo 16 2月 10 06:12 new.txt -rw-r--r-- 2 algo algo 16 2月 10 06:12 old.txt $4 = 0 guile>上とは対照的に,移動元のファイル(old.txt)と移動先のファイル(new.txt)が異なるファイルデータにリンクしている場合に何が起こるかを試しています. 移動操作の前に,両方のファイルのinode番号($2と$3)を求めて, 互いに異なるファイルデータにリンクしていることを確認しています. 移動操作(rename-file)を行うと, 移動元のハードリンク(old.txt)が削除されていることが分かります.
guile> (system "ls -l") -rw-r--r-- 1 algo algo 16 2月 10 06:25 new.txt -rw-r--r-- 1 algo algo 16 2月 10 06:25 old.txt $1 = 0 guile> (stat:ino (stat "old.txt")) $2 = 16916694 guile> (stat:ino (stat "new.txt")) $3 = 16917052 guile> (rename-file "old.txt" "new.txt") guile> (system "ls -l") -rw-r--r-- 1 algo algo 16 2月 10 06:25 new.txt $4 = 0 guile>
ファイルの転送
▹(sendfile out in count)(sendfile out in count offset) システムコールの sendfile 関数を使って,in から out へ count バイト分のデータを転送します.out と in はファイルポートかファイルディスクリプタです.count は転送バイト数を表す整数,offset はファイル内の位置を示す整数(バイト単位)です.この手続きの返り値は,実際に転送したバイト数です. offset を省略した場合,in の現在の位置から読み始めます.「現在の位置」は(たぶん)カーネルが管理しています. 一方,offset を指定した場合,offset が示す位置から読み始めます. sendfileシステムコールがうまく機能しない場合,read と write を使って転送を試みます.それから,転送速度が遅かったりした場合でも,count バイト数分の転送が完了するまでリトライします. ▹実行例:sendfile 手続きを使った次のスクリプトを実行します. これは,コマンドライン引数からファイル名(infile)を受け取って, そのファイルの中身を標準出力(current-output-port)に出力します.
#!/usr/bin/guile \ -e main -s !# ;; sendifle.scm: ;; -- send data from input file to standard output. (define (main args) (let* ((infile (cadr args)) (inport (open-input-file infile)) (count (stat:size (stat infile))) (offset 0)) (sendfile (current-output-port) inport count offset) (close-port inport)))今回の実行例に用いたファイル(somefile.txt)の中身は次の通りです.
------------------------------------- This is some text file prepared for testing sendfile.scm. -------------------------------------以下は実行結果です.
$ ./sendfile.scm somefile.txt ------------------------------------- This is some text file prepared for testing sendfile.scm. -------------------------------------▹(記録) 残念ながら次の技術的な背景が筆者には不明なので, マニュアルの文章をそのまま引用します.
When in is a port, it is often preferable to specify offset, because in's offset as a port may be different from the offset of its underlying file descriptor.in をファイルディスクリプタにするか,あるいはポートを利用するときには offset を維持管理しながら転送したほうがよいでしょう. ▹(記録)[Manpages:SENDFILE(2)]に次のような記述があります.
Because this copying is done within the kernel, sendfile() is more efficient than the combination of read(2) and write(2), which would require transferring data to and from user space.ファイル転送を行う場合,まずは sendfile による転送を検討したほうがよいでしょう.
ハードリンクの作成
▹(link old path new path) old path が示すファイルに対して,new path という名前の新たなハードリンクを作成します.old path と new path はファイル名を表す文字列です.返り値は unspecified です. old path が存在しない場合や,new path がすでに存在する場合,エラーが発生します.それから,ディレクトリに対してハードリンクを作ろうとしてもエラーが発生します.Linuxシステムでは,ディレクトリに対するハードリンクの作成は許されていません. old path がシンボリックリンクだったとき, (Linuxシステムでは)シンボリックリンクを辿らずに, シンボリックリンクファイル自体へのハードリンクを作成します.つまり, シンボリックリンクの先のファイルデータにハードリンクすることはありません. ▹実行例:既存のファイル(old.txt)に対して新たなハードリンク(new.txt)を作成してみます.作成の前後でカレントディレクトリの内容を確認しています.guile> (system "ls -l") -rw-r--r-- 1 algo algo 138 2月 10 12:08 old.txt $1 = 0 guile> (link "old.txt" "new.txt") guile> (system "ls -l") -rw-r--r-- 2 algo algo 138 2月 10 12:08 new.txt -rw-r--r-- 2 algo algo 138 2月 10 12:08 old.txt $2 = 0 guile>さらに以下では,両方のファイルのinode番号を調べて, 両者が同一のファイルデータであることを確認しています.
guile> (stat:ino (stat "old.txt")) $3 = 17039362 guile> (stat:ino (stat "new.txt")) $4 = 17039362 guile>▹実行例:以下では,old.txt へのシンボリックリンク(old.lnk)に対して,link 手続きを実行しています. 新たなファイル名を new.txt としていますが, それが old.txt と同一のファイルデータにハードリンクすることはなく,old.txt へのシンボリックリンクになっています.
guile> (system "ls -l") lrwxrwxrwx 1 algo algo 7 2月 10 12:35 old.lnk -> old.txt -rw-r--r-- 1 algo algo 138 2月 10 12:08 old.txt $1 = 0 guile> (link "old.lnk" "new.txt") guile> (system "ls -l") lrwxrwxrwx 2 algo algo 7 2月 10 12:35 new.txt -> old.txt lrwxrwxrwx 2 algo algo 7 2月 10 12:35 old.lnk -> old.txt -rw-r--r-- 1 algo algo 138 2月 10 12:08 old.txt $2 = 0 guile>さらに以下では,old.lnk と new.txt のinode番号を調べて, それらが同一のシンボリックリンクファイル(へのハードリンク)であることを確認しています.
guile> (stat:ino (stat "old.lnk")) $3 = 17039362 guile> (stat:ino (stat "new.txt")) $4 = 17039362 guile>▹(記録) この手続きの処理は, システムコールの link(old path,new path) によって行っています. 諸々の詳細については link システムコールのマニュアルを参照して下さい.
シンボリックリンクの作成
▹(symlink old path new path) old path を値とする new path という名前のシンボリックリンクを作成します.old path と new path はファイル名を表す文字列です. この手続きの返り値は unspecified です. old path がシンボリックリンクの場合,リンクは辿らずに, 上の説明の通りの処理を行います. new path がすでに存在する場合,エラーが発生します. ▹実行例:以下では,カレントディレクトリの変化を観察しながら, 既存のファイルに対してシンボリックリンクを作成しています. まず手始めに,old.txt へのシンボリックリンク(old.lnk)を作っています.guile> (system "ls -l") -rw-r--r-- 1 algo algo 138 2月 10 17:47 old.txt $1 = 0 guile> (symlink "old.txt" "old.lnk") guile> (system "ls -l") lrwxrwxrwx 1 algo algo 7 2月 10 18:02 old.lnk -> old.txt -rw-r--r-- 1 algo algo 138 2月 10 17:47 old.txt $2 = 0次に,old.lnk をターゲットとしたシンボリックリンク(new.lnk)を作っています. リンクは辿らずに,old.lnk へのリンクが作られます.
guile> (symlink "old.lnk" "new.lnk") guile> (system "ls -l") lrwxrwxrwx 1 algo algo 7 2月 10 18:03 new.lnk -> old.lnk lrwxrwxrwx 1 algo algo 7 2月 10 18:02 old.lnk -> old.txt -rw-r--r-- 1 algo algo 138 2月 10 17:47 old.txt $3 = 0最後に,上の作業で作成した(既存の) new.lnk を old.txt へのシンボリックリンクに変更しようとしています.残念ながら,new path が存在する場合,エラーが発生します.
guile> (symlink "old.txt" "new.lnk") ice-9/boot-9.scm:1669:16: In procedure raise-exception: In procedure symlink: ファイルが存在します Entering a new prompt. Type `,bt' for a backtrace or `,q' to continue. guile [1]>▹(記録) この手続きの処理は,システムコールの symlink(old path,new path) によって行われています.諸々の詳細は symlink システムコールのマニュアルを参照して下さい.
リンク先のファイル名の取得
▹(readlink path) path で指定されたシンボリックリンクの値(シンボリックリンクが指しているファイルやディレクトリの名前)を返します. path はシンボリックリンクのファイル名(文字列)です. ▹実行例:guile> (system "ls -l") lrwxrwxrwx 1 algo algo 7 2月 10 18:03 new.lnk -> old.lnk lrwxrwxrwx 1 algo algo 7 2月 10 18:02 old.lnk -> old.txt -rw-r--r-- 1 algo algo 138 2月 10 17:47 old.txt $1 = 0 guile> (readlink "old.lnk") $2 = "old.txt" guile> (readlink "new.lnk") $3 = "old.lnk" guile>▹(記録) 手続きの内部ではシステムコールの readlink(path,buf,size) を実行しています.buf は名前を受け取るためのバッファ,sizeはバッファの大きさ(バイト数)です.バッファ(buf)が不足している場合には, その大きさ(size)を増やしながら, 名前が正しく読み取れるまで繰り返し実行しています. 従って,readlink 手続きはリンク先の名前を正しく返してくれると思います.
ファイルやディレクトリの存在検査
▹(file-exists? path) path で指定されたファイルやディレクトリが存在しアクセスできるときには #t を返し,そうでないときには #f を返します.path はファイル名やディレクトリ名を表す文字列です. この手続きはシンボリックリンクを辿ります. 以下に示すように,この手続きは stat 手続きを使って属性値が取得できるか否かによって存在性を判定しています.従って,シンボリックリンクに対する振る舞いは stat 手続きと同じです. ▹実行例:以下の ls コマンドの結果が示すようなディレクトリ上で色々と試してみます.guile> (system "ls -l") lrwxrwxrwx 1 algo algo 16 2月 15 16:40 bbb.lnk -> temp-bbb/bbb.txt lrwxrwxrwx 1 algo algo 8 2月 16 07:07 dangling.lnk -> temp.txt lrwxrwxrwx 1 algo algo 12 2月 15 16:45 somefile.lnk -> somefile.txt -rw-r--r-- 1 algo algo 22 2月 15 16:44 somefile.txt drwxr-xr-x 2 algo algo 4096 2月 15 16:40 temp-aaa lrwxrwxrwx 1 algo algo 8 2月 15 16:56 temp-aaa-lnk -> temp-aaa drwxr-xr-x 2 algo algo 4096 2月 15 16:40 temp-bbb $1 = 0
guile> (file-exists? "somefile.txt") $2 = #t guile> (file-exists? "somefile.lnk") $3 = #t guile> (file-exists? "temp-aaa") $4 = #t guile> (file-exists? "temp.txt") $5 = #f guile> (file-exists? "dangling.lnk") $6 = #f guile> (file-exists? "temp-ccc") $7 = #f guile>▹(記録) この手続きは,(ice-9 boot-9)モジュールの中で次のように定義されています(注:意味を変えずに少しだけ書き換えています).
;; For reference, Emacs file-exists-p uses stat in this same way. (define file-exists? (if (provided? 'posix) (lambda (str) (not (not (stat str #f)))) (lambda (str) (let ((port (catch 'system-error (lambda () (open-input-file str)) (lambda args #f)))) (if port (begin (close-port port) #t) #f)))))provided? 手続きはUNIX系システムかどうかを判定していて,UNIX系システムの場合, (provided? 'posix) は #t を返します.従って,Linuxの場合,file-exists? 手続きは次のように定義されていることになります.なお,二重否定は真値を #t に変換するためのものです.
(define file-exists? (lambda (str) (not (not (stat str #f)))))この手続きは,str によって指定されたファイルやディレクトリの属性値が取得できたときには #t を返し,何らかの理由によって取得できなかったときには #f を返します.そのため,この手続きはファイルやディレクトリの存在性を近似的に検査していると言えます.例えば,ファイルが存在していてもアクセス権限がないときには #f を返します.#f を返す条件については,システムコールの stat 関数のエラー条件 (例えば, [Manpages:STAT(2)]のERRORなど)を参照して下さい.