ハマったので後学のためにメモを残しています。うえ〜ん
一時的にファイルシステムにファイルやディレクトリを作りたい、ということはよくある。ただし適当な名前で作成すると名前が被って事故の元になってしまう。このため、各プログラミング言語には一時ファイル・ディレクトリを作成する専用のAPIが用意されていることが多い(libcにmktemp(3)
が用意されているため、最終的にこれを呼び出しているのではないか)。
Node.jsのfs
モジュールもこの機能を提供しているのだが、prefix
を指定するとき以下の点に注意する必要がある。
prefix
には/tmp/
も含める必要がある
つまり以下のように使う必要があるということ:
import fs from 'fs'; const tempFile = fs.mkdtempSync('/tmp/foobar-'); // => /tmp/foobarABCDEF // ... fs.rmdirSync(tempFile);
以下のように書くと権限不足でコケる(意図しない場所に書き込もうとしてしまう)
import fs from 'fs'; const tempFile = fs.mkdtempSync('foobar-'); // => Error: EACCES: permission denied, mkdtemp 'foobar-XXXXXX' // ... fs.rmdirSync(tempFile);
一見エラー時にどこに書き込もうとしたか分からないので注意が必要だ。
なんでこんなAPIにしたんだよ!!!!!
他の言語だとどうなってんの
Scalaのos-lib
だとprefix
指定に/tmp
を含める必要はない:
//> using scala 3.3.1 //> using toolkit latest val tmp = os.temp.dir(prefix = "mktemp-") println(tmp) // => /tmp/mktemp-8415820769307691913
Rubyでもprefix
指定とディレクトリは分離されている:
require 'tmpdir' Dir.mktmpdir("foo"){|dir| puts dir # => /tmp/foo20231226-1057143-rj7zc }
Pythonでも同様:
import tempfile import os with tempfile.TemporaryDirectory(prefix="foobar") as dname: print(dname) # => /tmp/foobar9y6ri4j3
Goではディレクトリ指定を空文字列にすると自動的に/tmp
などが利用される:
package main import ( "log" "os" "path/filepath" ) func main() { dir, err := os.MkdirTemp("", "example") // => /tmp/example123456 if err != nil { log.Fatal(err) } defer os.RemoveAll(dir) // clean up }
Rustにはmktemp
というcrateがあるが、prefix指定はできなそう。
まとめ
NodeのAPIだけなんか奇妙なことになっているので気をつけてね