Lambdaカクテル

京都在住Webエンジニアの日記です

Invite link for Scalaわいわいランド

Node.jsのmkdtempはprefix指定が独特で注意が必要だけど、他の言語は大体普通だった

ハマったので後学のためにメモを残しています。うえ〜ん

一時的にファイルシステムにファイルやディレクトリを作りたい、ということはよくある。ただし適当な名前で作成すると名前が被って事故の元になってしまう。このため、各プログラミング言語には一時ファイル・ディレクトリを作成する専用の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指定はできなそう。

docs.rs

まとめ

NodeのAPIだけなんか奇妙なことになっているので気をつけてね

★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?