Gazelle
2020年10月17日公開 2937 Views

writeFileSyncは実行ディレクトリからの相対パスで注意が必要

nodejsのrequireは実行されるファイルからの相対パスが使われるのに対して、writeFileSyncはファイルを実行したディレクトリからの相対パスが使われるので混同していたらハマることになる。

これをサンプルコードを元に示す。

サンプルコード

main.jsgreet.jsnodetestディレクトリ以下に作成する。それぞれコードは下記。

main.js

const fs = require('fs');
const Greet = require('./greet.js');
Greet.sayHello();
fs.writeFileSync('./data/data.txt', 'Hello');

dataディレクトリは別途作成しておく。

greet.js

module.exports =  {
  sayHello: () => {
    console.log('hello');
  }
};

これをnodetestディレクトリから実行

[yamanaka@118-27-1-231 nodetest]$ node main.js
hello

問題なく実行される。

それではこれを一つ上の階層から実行する。

node nodetest/main.js 
hello
internal/fs/utils.js:299
    throw err;
    ^

Error: ENOENT: no such file or directory, open './data/data.txt'
    at Object.openSync (fs.js:474:3)
    at Object.writeFileSync (fs.js:1436:35)
    at Object.<anonymous> (/home/yamanaka/public_html/nodetest/main.js:4:4)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
    at internal/main/run_main_module.js:17:47 {
  errno: -2,
  syscall: 'open',
  code: 'ENOENT',
  path: './data/data.txt'
}

helloは出力されているので、requireでモジュールは正しくインポートができている。しかしwriteFileSyncは実行ディレクトリからの相対パスなので、そのディレクトリにはdataディレクトリが存在しない

よってエラーが起きてしまう。

まとめ

writeFileSyncに限らずファイル保存系の関数が、nodeファイルではなく、nodeファイルを実行したディレクトリをベースとしていることは、普段の開発であれば問題とはならないが、ciツールでのビルドや、cronで回す時などにはハマり得るポイントかと思われる。

必ずcd nodetestなど所定のディレクトリにまず行ってから実行しよう。

ホームへ戻る