読者です 読者をやめる 読者になる 読者になる

Minecraftとタートルと僕

PCゲームMinecraftのMOD「ComputerCraft」の情報を集めたニッチなブログです。

FS API を使いこなそう(3): FS APIの解説

はじめに

前回は、コンピュータのシェル上で実行できるプログラムの紹介をしました。

今回はLuaプログラムの中で利用できるファイルシステム系のAPI、すなわちFS APIについて解説しましょう。

まずはFS APIについてざっと説明し、その後、個別に使い方のサンプルも示しつつ説明しましょう。

なおfs.open()関数については、次回解説する予定です。

Functions in the Filesystem API

  • 引数のpath/rom/help/fsのように絶対パス(文字列)で表記するのが原則
    • ただし、rom/help/fsのように、頭の/を省略してもよい
    • rom/help/のようにパスの末尾が/であったら、それはディレクトリである
    • rom/helpのようにパスの末尾が/でなかったら、ファイルかもしれないしディレクトリかもしれない。つまり、曖昧表現も可。
  • 引数のwildcardとは*という記号のことであり、パス中の任意の文字列を表現できる
    • 例:rom/programs/red*rom/*/red*
関数名 引数・返値、機能の説明
fs.list(path) 引数(文字列):ディレクトリのパス
返値(テーブル):ファイル・ディレクトリのパス(複数)
指定したディレクトリ中にあるファイル・ディレクトリのパスをすべて返す。ただし、中にあるディレクトリのさらに中のディレクトリ(サブディレクトリ)までは探さない。
fs.find( wildcard ) 引数(文字列):ワイルドカード(*)入りのパス
返値(テーブル):ファイル・ディレクトリのパス(複数)
存在するパスの中で、ワイルドカードにあてはまるパスをすべて返す。CC1.6より追加。
fs.exists(path) 引数(文字列):パス
返値(boolean):true(存在する)/false(存在しない)
指定したパスが存在するかどうかをtrue/falseで返す。
fs.isDir(path) 引数(文字列):パス
返値(boolean):true(ディレクトリである)/false(ディレクトリでない)
指定したパスがディレクトリであるかどうかをtrue/falseで返す。
fs.isReadOnly(path) 引数(文字列):パス
返値(boolean):true(読み専用)/false(読み専用ではない)
指定したパスが読み専用であるかどうかをtrue/falseで返す。たとえば/rom/は読み専用。
fs.getName(path) 引数(文字列):パス
返値(文字列):パス末尾のファイル・ディレクトリ名
与えたパスの末尾にあるファイル・ディレクトリ名を返す。
fs.getDir(path) 引数(文字列):パス
返値(文字列):(一つ上の?)ディレクトリ名
与えたパスの末尾にあるファイル・ディレクトリ名を取り除いた残りの部分。CC1.63より追加。
fs.combine(path, localpath ) 引数1(文字列):パス、 引数2(文字列):パス
返値(文字列):二つのパスを連結したパス
二つのパスを連結したパスを返す。つまり、
path == fs.combine(fs.getDir(path), fs.getName(path))
fs.getSize(path) 引数(文字列):パス
返値(数値):ファイルのサイズ(byte)
与えたパスがファイルならばそのサイズ(byte)を返す。なお、ディレクトリならばサイズは0を返す。
fs.getDrive(path) 引数(文字列):パス
返値(文字列):保存されているドライブ(機器・場所)の名前
通常のパスは「hdd」。/rom/以下ならば「rom」。フロッピーならば「方向あるいはネットワーク名」となる。
fs.getFreeSpace(path) 引数(文字列):パス
返値(数値):空き容量(byte)
そのパス(正確にはドライブ)にあとどのくらいの空き容量があるか。
fs.makeDir(path) 引数(文字列):ディレクトリのパス
返値なし
指定したディレクトリを作成する。なおパス途中の存在しないディレクトリも一緒に作成してくれる。
fs.move(path, path) 引数1(文字列):元パス、 引数2(文字列):目的パス
返値なし
元パスから目的パスへファイル・ディレクトリを移動する。
fs.copy(path, path) 引数1(文字列):元パス、 引数2(文字列):目的パス
返値なし
元パスから目的パスへファイル・ディレクトリをコピーする。
fs.delete(path) 引数(文字列):パス
返値なし
指定パスのファイル・ディレクトリを削除する。なお、そのサブディレクトリもすべて一度に削除するので取扱い注意。
fs.open(path, mode ) 引数1(文字列):ファイルパス、 引数2(文字列):ファイルを開くモード
返値(テーブル):ファイルハンドル(詳しくは次回)
指定したファイルを指定したモードで開き、そのファイルの読み書きを仮想的に取り扱うファイルハンドルを返す。

FS APIの解説(分類ごと)

以下に示す使用例において、lua>とあるのはLua interactive mode(シェルで「lua」と打ち込む)を使っていることを示しています。

そうでないものは、Luaプログラムです。

ファイルやディレクトリを一覧表示する系統の関数

fs.list(path)

  • 指定したディレクトリ中にあるファイル・ディレクトリのパスをすべて返す。ただし、中にあるディレクトリのさらに中のディレクトリ(サブディレクトリ)までは取り扱わない。
    • 引数(文字列):ディレクトリのパス
    • 返値(テーブル):ファイル・ディレクトリのパス(複数)
# lua interactive mode
lua> fs.list("rom")
apis autorun help programs startup

fs.find( wildcard )

  • 存在するパスの中で、ワイルドカードにあてはまるパスをすべて返す。CC1.6より追加。
    • 引数(文字列):ワイルドカード(*)入りのパス
    • 返値(テーブル):ファイル・ディレクトリのパス(複数)
# lua interactive mode
lua> tbl = fs.find("/rom/*/gps*)
# tblの中身
{"rom/apis/gps",
"rom/help/gps",
"rom/programs/gpsapi",
"rom/programs/gps"}

結果をtrue/falseで返すような、test系の関数

fs.exists(path)

  • 指定したパスが存在するかどうかをtrue/falseで返す。
    • 引数(文字列):パス
    • 返値(boolean):true(存在する)/false(存在しない)

最もよく使うであろう関数。新しくファイルを作るときに同じ名前のファイルがあったら困るので、たいていはこれを最初に使いますよね。

# すでに"logfile.txt"があるならばエラーでプログラム停止します。
if fs.exists("logfile.txt") then
  error("file exists!")
end

fs.isDir(path)

  • 指定したパスがディレクトリであるかどうかをtrue/falseで返す。
    • 引数(文字列):パス
    • 返値(boolean):true(ディレクトリである)/false(ディレクトリでない)
# lua interactive mode
lua> fs.isDir("rom/help")
true

fs.isReadOnly(path)

  • 指定したパスが読み専用であるかどうかをtrue/falseで返す。たとえば/rom/は読み専用。
    • 引数(文字列):パス
    • 返値(boolean):true(読み専用)/false(読み専用ではない)
# lua interactive mode
lua> fs.isReadOnly("rom/")
true

lua> fs.isReadOnly("/")
false

パス文字列を取り扱うための関数

fs.getName(path)

  • 与えたパスの末尾にあるファイル・ディレクトリ名を返す。
    • 引数(文字列):パス
    • 返値(文字列):パス末尾のファイル・ディレクトリ名

fs.getDir(path)

  • 与えたパスの末尾にあるファイル・ディレクトリ名を取り除いた残りの部分。CC1.63より追加。
    • 引数(文字列):パス
    • 返値(文字列):(一つ上の?)ディレクトリ名

fs.combine(path, localpath )

  • 二つのパスを連結したパスを返す。
    • 引数1(文字列):パス、 引数2(文字列):パス
    • 返値(文字列):二つのパスを連結したパス

この3つの関数は以下のようにセットで覚えると良いかもしれません。

  • fs.getName(path)で、パスの末尾部分
  • fs.getDir(path)で、パスから末尾部分を取り除いた残りのパス
  • パスの末尾(fs.getName)と残りの部分(fs.getDir)を連結(fs.combine)すると元通り
# lua interactive mode
lua> path = "rom/help/gps"

lua> fs.getName(path)
gps

lua> fs.getDir(path)
rom/help

lua> path == fs.combine(fs.getDir(path), fs.getName(path))
true

ファイルサイズやドライブ名、ドライブの空き容量を調べる関数

fs.getSize(path)

  • 与えたパスがファイルならばそのサイズ(byte)を返す。なお、ディレクトリならばサイズは0を返す。
    • 引数(文字列):パス
    • 返値(数値):ファイルのサイズ(byte)

fs.getDrive(path)

  • 通常のパスは「hdd」。/rom/以下ならば「rom」。フロッピーならば「方向あるいはネットワーク名」となる。
    • 引数(文字列):パス
    • 返値(文字列):保存されているドライブ(機器・場所)の名前

fs.getFreeSpace(path)

  • そのパス(正確にはドライブ)にあとどのくらいの空き容量があるか。
    • 引数(文字列):パス
    • 返値(数値):空き容量(byte)
# lua interactive mode
lua> fs.getSize("rom/help/gps")
(gpsファイルのサイズ)

lua> fs.getDrive("rom/help/gps")
"rom"

lua> fs.getFreeSpace("/")
(設定ファイルで設定した容量が返ってくるはず)

ファイル・ディレクトリ操作系の関数

fs.makeDir(path)

  • 指定したディレクトリを作成する。なおパス途中の存在しないディレクトリも一緒に作成してくれる。 |
    • 引数(文字列):ディレクトリのパス
    • 返値なし

fs.move(path, path)

  • 元パスから目的パスへファイル・ディレクトリを移動する。
    • 引数1(文字列):元パス、 引数2(文字列):目的パス
    • 返値なし

fs.copy(path, path)

  • 元パスから目的パスへファイル・ディレクトリをコピーする。
    • 引数1(文字列):元パス、 引数2(文字列):目的パス
    • 返値なし

fs.delete(path)

  • 指定パスのファイル・ディレクトリを削除する。なお、そのサブディレクトリもすべて一度に削除するので取扱い注意。
    • 引数(文字列):パス
    • 返値なし

これらの関数は、システムプログラムである「move」「copy」「delete」の中でも使われています。

強力な機能(サブディレクトリも一度に作る、サブディレクトリも含めて一気に消す)を持つので使用には細心の注意を。

それと、CC作者さーん。これらの関数が実行成功したときにも返値なしというのはとても使いづらいんですけど! 普通はtrueを返すんじゃないの?

# もしディレクトリがなかったら作る。
if not fs.exists("tmp/tmp1/tmp2") then
  # 途中のサブディレクトリがなかったら、一気にディレクトリを作ってくれます
  fs.makeDir("tmp/tmp1/tmp2")
end

# "rom/help/gps"ファイルを"tmp/"以下にコピー
if fs.exists("tmp/") then
  fs.copy("rom/help/gps", "tmp/gps")

# moveを使うとファイル名の変更もできます。
if fs.exists("tmp/gps") then
  fs.move("tmp/gps", "tmp/gps2")
end

# サブディレクトリや中にあるファイルも含めて全部消すよ!
fs.delete("tmp")

次回のお話

FS APIにはもう一つ、fs.open()というとても重要な関数があります。

典型的なファイルハンドルを返す関数なのですが、プログラム経験の少ない人にとって、ファイルハンドルと言われても困りますよね。

次回、具体的な使用例も示しつつ解説しましょう。