ファイルへの書き込み処理などは予定どおり完了するとは限らない。 実際には書き込み権限がない場合などがあるため, 事前にファイルに関連する条件を調査することが重要である。 ファイルがあるかないか,読めるか,書けるか,空か,など 指定したファイルの状態を調べるための組み込み関数 test を利用してこれらの条件判定を行なう。。
test
の用法test
は,
test(cmd, file1 [, file2])
の書式で用い,指定したファイル file1 が cmd の要件を満たすかどうかを返す。2つのファイルの 関係を調べるための cmd には第3引数 file2 を与える。
cmd | 働き |
---|---|
?r | 現在の実行uidで読めるか |
?w | 現在の実行uidで書き込めるか |
?x | 現在の実行uidで実行できるか |
?o | 現在の実行uidが所有するファイルか |
?G | 現在の実行gidと同じグループ所有か |
?R | 実 uid で読めるか |
?W | 実 uid で書き込めるか |
?X | 実 uid で実行できるか |
?O | 実 uid が所有するファイルか |
?e | 存在するか |
?s | 0バイトでないか(サイズが返る) |
?f | 普通のファイルか |
?d | ディレクトリか |
?l | シンボリックリンクか |
?p | 名前付きパイプ(FIFO)か |
?b | ブロックデバイスファイルか |
?c | キャラクタデバイスファイルか |
?u | setuidビットがセットされているか |
?g | setgidビットがセットされているか |
?k | stickyビットがセットされているか |
?M | 最終更新時刻を返す |
?A | 最終アクセス時刻を返す |
?C | inode時刻を返す |
以下は第2,第3引数間の比較 | |
?- | 両ファイルが同一か |
?= | 両ファイルの最終更新時刻が等しいか |
?> | file1の 最終更新時刻がfile2より大きい(新しい) |
?< | file1の 最終更新時刻がfile2より小さい(古い) |
test
の利用例実行結果を決まった名前のファイルに保存するようなコマンドを考える。
ここでは単純なものとしてたとえば,
おみくじの結果をカレントディレクトリの fortune.txt
に保存するプログラムを考える。
#!/usr/bin/env ruby # -*- coding: utf-8 -*- #Foretune teller - version 0 ftune = %w,大吉 吉 中吉 小吉 半吉 末吉 小凶 凶, outfile = "fortune.txt" srand result = ftune[rand(ftune.length)] open(outfile, "w") do |f| f.printf("今日の運勢は %s\n", result) end STDERR.puts "#{outfile}を見よ。"
起動して,foretune.txt ファイルを確認してみる。
./ft0.rb fortune.txtを見よ。 ls -l fortune.txt -rw-r--r-- 1 yuuji wheel 26 Mar 14 07:58 fortune.txt cat fortune.txt 今日の運勢は 中吉
もう一度実行すると上書きされる。
./ft0.rb fortune.txtを見よ。 cat fortune.txt 今日の運勢は 大吉
せっかく大吉が出たので上書きされないよう chmod で書き込み禁止にする。
chmod -w fortune.txt ls -l fortune.txt -r--r--r-- 1 yuuji wheel 26 Mar 14 07:58 fortune.txt
この状態で実行するとプログラムはエラー終了する。
./ft0.rb
ft0.rb:8:in `initialize': Permission denied @ rb_sysopen - fortune.txt (Errno::EACCES)
from ft0.rb:8:in `open'
from ft0.rb:8:in `<main>'
以上の様子から,次のような問題があることが分かる。
これらの問題を解決するなら以下のような改良を施すことになるだろう。
上記2点の改良を施してみよ。
あるディレクトリ以下にあるすべてのファイルに対して特定の処理を 行なうことはしばしば必要となる。そのための手順の流れは 以下のようになる。
特定の処理をメソッド化し,処理対象がディレクトリなら再帰処理を行なう。 以下のプログラムは,指定したディレクトリ (省略時はカレントディレクトリ)以下すべてのファイルに対して, それが通常ファイルならバックアップファイルを作成する。。
#!/usr/bin/env ruby # -*- coding: utf-8 -*- dir = ARGV.shift || "." def cp(from, to, bufsize=8192) open(from, "r") do |src| open(to, "w") do |dest| while s = src.read(bufsize) dest.write s end end end end def bakdir(dir) ext = ".bak" Dir.foreach(dir) do |f| file = File.expand_path(f, dir) case f when ".", ".." # カレントディレクトリと親ディレクトリ next # は飛ばす when %r,#{ext}$, # それが既にバックアップファイルなら next # やはり飛ばす else if test(?d, file) # ディレクトリなら bakdir(file) # 再帰的に自分を呼ぶ elsif test(?f, file) # 通常ファイルなら # バックアップファイル作成 bak = File.expand_path(f+".bak", dir) File.unlink(bak) if test(?e, bak) cp(file, bak) end end end end bakdir(dir)