ノードのfs.mkdirSyncでフルパスを作成する方法


159

存在しない場合はフルパスを作成しようとしています。

コードは次のようになります。

var fs = require('fs');
if (!fs.existsSync(newDest)) fs.mkdirSync(newDest); 

このコードは、サブディレクトリが1つ( 'dir1'のようなnewDest)しかない限り問題なく機能しますが、( 'dir1 / dir2')のようなディレクトリパスがあると、エラー:ENOENTで失敗し 、そのようなファイルやディレクトリはありません。

必要なだけのコード行でフルパスを作成できるようにしたいと思います。

私はfsに再帰的なオプションがあると読んで、このようにしてみました

var fs = require('fs');
if (!fs.existsSync(newDest)) fs.mkdirSync(newDest,'0777', true);

存在しないディレクトリを再帰的に作成するのは簡単だと思います。何かが足りないのですか、それともパスを解析して各ディレクトリを確認し、まだ存在しない場合は作成する必要がありますか?

私はノードを初めて使用します。古いバージョンのFSを使用しているのではないですか?


1
github.com/substack/node-mkdirpと、このGoogle検索での他のあらゆる種類のソリューション。
jfriend00 2015

4
@AndyRayこのStackOverflowの質問は今のrecursiことを意味するから面白いです、この質問のためのGoogleのトップ結果である....
マット・パーキンス

1
これは以前のバージョンのノードでは問題でしたが、ノード12+に更新すると問題が解決します
MrJomp

回答:


48

1つのオプションは、shelljsモジュールを使用することです

npm install shelljs

var shell = require('shelljs');
shell.mkdir('-p', fullPath);

そのページから:

利用可能なオプション:

p:フルパス(必要に応じて中間ディレクトリを作成します)

他の人が指摘しているように、他にも焦点を絞ったモジュールがあります。しかし、mkdirpの外では、他にもたくさんの便利なシェル操作(with、grepなど...)があり、Windowsと* nixで動作します


2
ありがとう!私はexecを使用することになり(私はすでにこれを使用していました)、それは魅力のように機能しました。var exec = require( 'child_process')。exec; var command = "mkdir -p '" + newDest + "'"; var options = {}; var after = function(error、stdout、stderr){console.log( 'error'、error); console.log( 'stdout'、stdout); console.log( 'stderr'、stderr); } exec(コマンド、オプション、後);
David Silva Smith

24
このオプションは、コマンドラインのmkdirインスタンスを持たないnode.jsプラットフォーム(つまり、Linux-y以外のホスト)では機能しない可能性があるため、問題がある場合は移植できません。
cshotton 2016

1
@cshotton-コメントまたは回答のどちらを参照していますか?shelljsはWindowsでも機能します。exec mkdir -p(コメント)はもちろんできません。
bryanmac

このクールな関数は、選択したPromiseまたはコールバックで使用できます。
ИльяЗеленько

1
これは解決策ではありません。これは解決策の代替手段です。コンテキスト:pics.onsizzle.com/...
ニカKasradze

413

編集する

NodeJSのバージョンは10.12.0、両方のネイティブサポートを追加しましたmkdirし、mkdirSyncして再帰的にディレクトリを作成するために、recursive: true以下のようなオプション:

fs.mkdirSync(targetDir, { recursive: true });

そして、あなたが望むなら fs Promises API、あなたは書くことができます

fs.promises.mkdir(targetDir, { recursive: true });

元の回答

ディレクトリが存在しない場合は、再帰的に作成してください!(依存関係なし

const fs = require('fs');
const path = require('path');

function mkDirByPathSync(targetDir, { isRelativeToScript = false } = {}) {
  const sep = path.sep;
  const initDir = path.isAbsolute(targetDir) ? sep : '';
  const baseDir = isRelativeToScript ? __dirname : '.';

  return targetDir.split(sep).reduce((parentDir, childDir) => {
    const curDir = path.resolve(baseDir, parentDir, childDir);
    try {
      fs.mkdirSync(curDir);
    } catch (err) {
      if (err.code === 'EEXIST') { // curDir already exists!
        return curDir;
      }

      // To avoid `EISDIR` error on Mac and `EACCES`-->`ENOENT` and `EPERM` on Windows.
      if (err.code === 'ENOENT') { // Throw the original parentDir error on curDir `ENOENT` failure.
        throw new Error(`EACCES: permission denied, mkdir '${parentDir}'`);
      }

      const caughtErr = ['EACCES', 'EPERM', 'EISDIR'].indexOf(err.code) > -1;
      if (!caughtErr || caughtErr && curDir === path.resolve(targetDir)) {
        throw err; // Throw if it's just the last created dir.
      }
    }

    return curDir;
  }, initDir);
}

使用法

// Default, make directories relative to current working directory.
mkDirByPathSync('path/to/dir');

// Make directories relative to the current script.
mkDirByPathSync('path/to/dir', {isRelativeToScript: true});

// Make directories with an absolute path.
mkDirByPathSync('/path/to/dir');

デモ

それを試してみてください!

解説

  • [更新]このソリューションはEISDIR、MacやEPERMそしてEACCESWindows用。@PediT。、@ JohnQ、@ deed02392、@ robyoder、@ Almenonのすべてのレポートコメントに感謝します。
  • このソリューションは、相対パス絶対パスのます。@johnコメントに感謝します。
  • 相対パスの場合、ターゲットディレクトリは現在の作業ディレクトリに作成(解決)されます。現在のスクリプトディレクトリに関連してそれらを解決するには、{isRelativeToScript: true}ます。
  • クロスプラットフォームの問題を回避するために、単なる連結ではなくpath.sepとを使用します。path.resolve()/
  • ifがスローされて競合状態を処理fs.mkdirSyncするtry/catch場合のエラーの使用と処理:別のプロセスがfs.existsSync()との呼び出しの間にファイルを追加するfs.mkdirSync()と、例外が発生します。
    • これを実現するもう1つの方法は、ファイルが存在するかどうかを確認してから作成することif (!fs.existsSync(curDir) fs.mkdirSync(curDir);です。しかし、これはコードが競合状態に対して脆弱なままになるアンチパターンです。ディレクトリの存在チェックに関する@GershomMaesコメントに感謝します。
  • 破壊をサポートするには、Node v6以降が必要です。(古いNodeバージョンでこのソリューションの実装に問題がある場合は、コメントを残してください)

7
追加のライブラリやアプローチを必要としない簡単で再帰的な応答に賛成投票してください!
MikingTheViking

1
requireステートメントがない:const fs = require( 'fs'); const path = require( 'path');
クリストファーブル

1
@ChristopherBull、ロジックに焦点を合わせるために意図的に追加されたのではなく、とにかくそれらを追加しました。ありがとう;)
Mouneer 2017

1
12行の確実なコード、依存関係はありません。毎回取り上げます。
moodboom 2017

1
Mac OS X 10.12.6の@Mouneerで、絶対パスを渡した後に「/」を作成しようとするとスローされるエラーは「EISDIR」です(エラー:EISDIR:ディレクトリでの不正な操作、mkdir '/')。おそらく、dirの存在を確認することは、クロスプラットフォームでの最良の方法です(速度が遅くなることを認めます)。
ジョンQ

78

より堅牢な答えは、use mkdirpを使用することです

var mkdirp = require('mkdirp');

mkdirp('/path/to/dir', function (err) {
    if (err) console.error(err)
    else console.log('dir created')
});

次に、次のようにしてファイルをフルパスに書き込みます。

fs.writeFile ('/path/to/dir/file.dat'....

ライブラリ全体ではなく、必要なものだけをインポートするので、この回答をお勧めします。
Juan Mendes

1
ポピュリストバッジ
おめでとう

1
ありがとう。それは最も良い方法です。
ステパンラファエル


48

fs-extraは、ネイティブfsモジュールに含まれていないファイルシステムメソッドを追加します。これはfsの代わりのドロップです。

インストール fs-extra

$ npm install --save fs-extra

const fs = require("fs-extra");
// Make sure the output directory is there.
fs.ensureDirSync(newDest);

同期と非同期のオプションがあります。

https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir.md


5
これが一番の答えです!とにかく、ほとんどの人はすでにfs-extraをアプリに持っています。
pagep '19年

memfs単体テストに使用する可能性がある場合、これは素晴らしいことです。それはしません:-( github.com/jprichardson/node-fs-extra/issues/274
schnatterer

31

reduceを使用すると、各パスが存在するかどうかを確認し、必要に応じて作成できます。この方法でも、従う方が簡単だと思います。@Arvinに感謝します。適切なプラットフォーム固有のパスセグメントセパレーターを取得するには、path.sepを使用する必要があります。

const path = require('path');

// Path separators could change depending on the platform
const pathToCreate = 'path/to/dir'; 
pathToCreate
 .split(path.sep)
 .reduce((prevPath, folder) => {
   const currentPath = path.join(prevPath, folder, path.sep);
   if (!fs.existsSync(currentPath)){
     fs.mkdirSync(currentPath);
   }
   return currentPath;
 }, '');

4
答えを出すとき、なぜあなたの答えがその答えであるのかについていくつかの説明を与えることが望ましいです。
スティーブンラウフ2017

申し訳ありませんが、あなたは正しいです。私はこの方法がよりクリーンでフォローしやすいと思います
josebui

4
@josebui環境固有の問題を回避するには、スラッシュ(/)の代わりに "path.sep"を使用する方が良いと思います。
Arvin

他の回答のようにノード> = 10を必要としないため、適切なソリューション
Karim

29

この機能はバージョン10.12.0のnode.jsに追加されたため、オプション{recursive: true}fs.mkdir()呼び出しの2番目の引数として渡すだけで簡単です。公式ドキュメントの例をご覧ください。

外部モジュールや独自の実装は必要ありません。


1
関連するプルリクエストgithub.com/nodejs/node/pull/23313
nurettinを

1
ディレクトリが存在し、停止するとエラーがスローされます。try catchブロックを使用すると、他の存在しないフォルダーを作成し続けることができます。
Choco Li

1
これは受け入れられる答えになるはずです。ディレクトリがすでに存在する場合はスローせず、fs.promises.mkdirを介してasync / awaitで使用できます。
リッチアポダカ

7

私はこれが古い質問であることを知っていますが、nodejs v10.12.0はrecursiveオプションをtrueに設定してネイティブにサポートするようになりました。fs.mkdir

// Creates /tmp/a/apple, regardless of whether `/tmp` and /tmp/a exist.
fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => {
  if (err) throw err;
});


2

Windowsの例(追加の依存関係およびエラー処理なし)

const path = require('path');
const fs = require('fs');

let dir = "C:\\temp\\dir1\\dir2\\dir3";

function createDirRecursively(dir) {
    if (!fs.existsSync(dir)) {        
        createDirRecursively(path.join(dir, ".."));
        fs.mkdirSync(dir);
    }
}

createDirRecursively(dir); //creates dir1\dir2\dir3 in C:\temp

2

フォルダーが存在するか、パスにないかを再帰的に確認し、フォルダーが存在しないかどうかを確認しながらフォルダーを作成できます。(外部ライブラリなし

function checkAndCreateDestinationPath (fileDestination) {
    const dirPath = fileDestination.split('/');
    dirPath.forEach((element, index) => {
        if(!fs.existsSync(dirPath.slice(0, index + 1).join('/'))){
            fs.mkdirSync(dirPath.slice(0, index + 1).join('/')); 
        }
    });
}

2

次の機能が使えます

const recursiveUpload =(path:string)=> {const paths = path.split( "/")

const fullPath = paths.reduce((accumulator, current) => {
  fs.mkdirSync(accumulator)
  return `${accumulator}/${current}`
  })

  fs.mkdirSync(fullPath)

  return fullPath
}

だからそれは何をします:

  1. paths変数を作成し、配列の要素としてすべてのパスを単独で格納します。
  2. 配列の各要素の最後に「/」を追加します。
  3. サイクルを作ります:
    1. インデックスが0から現在の反復までの配列要素の連結からディレクトリを作成します。基本的に、それは再帰的です。

お役に立てば幸いです。

ちなみに、Node v10.12.0では、追加の引数として指定することで、再帰的なパス作成を使用できます。

fs.mkdir('/tmp/a/apple', { recursive: true }, (err) => { if (err) throw err; });

https://nodejs.org/api/fs.html#fs_fs_mkdirsync_path_options


1

答えは多すぎますが、パスを分割して左から右に構築して再構築することで機能する再帰のない解決策を次に示します

function mkdirRecursiveSync(path) {
    let paths = path.split(path.delimiter);
    let fullPath = '';
    paths.forEach((path) => {

        if (fullPath === '') {
            fullPath = path;
        } else {
            fullPath = fullPath + '/' + path;
        }

        if (!fs.existsSync(fullPath)) {
            fs.mkdirSync(fullPath);
        }
    });
};

WindowsとLinuxの互換性が気になる方は、上記の両方でスラッシュをダブルバックスラッシュ '\'に置き換えてください。 Windowsおよびより完全なソリューションのクロスプラットフォームです。


ウィンドウ上のファイルは、スラッシュではなくバックスラッシュで処理されます。あなたのコードはそこで機能しません。C:\ data \ test ...
DDD

編集されましたが、コメントを検証することをお勧めします。ノードで次を試して、何が起こるかを確認してくださいvar fs = require( 'fs')fs.mkdirSync( 'test')fs.mkdirSync( 'test \\ test1')fs.mkdirSync( 'test / test2')
Hamiora

あなたが何を言っていても..、あなたがより良いコードを書くことを学ぶまで、私の反対票は残ります。
DDD

はは。わかりました、私はより良いコードを書く方法を学ぶために一生懸命働きます。ところで、OPを含む上記のほとんどの回答では、スラッシュを使用しています。トローリングをやめることを提案します。
ハミオラ2018年

1
path.sep/または\\のどちらかとして私に届きます。path.delimiterは:または;です。
ジョシュアンダーソンスレート

1
const fs = require('fs');

try {
    fs.mkdirSync(path, { recursive: true });
} catch (error) {
    // this make script keep running, even when folder already exist
    console.log(error);
}

0

ディレクトリを再帰的に作成する非同期の方法:

import fs from 'fs'

const mkdirRecursive = function(path, callback) {
  let controlledPaths = []
  let paths = path.split(
    '/' // Put each path in an array
  ).filter(
    p => p != '.' // Skip root path indicator (.)
  ).reduce((memo, item) => {
    // Previous item prepended to each item so we preserve realpaths
    const prevItem = memo.length > 0 ? memo.join('/').replace(/\.\//g, '')+'/' : ''
    controlledPaths.push('./'+prevItem+item)
    return [...memo, './'+prevItem+item]
  }, []).map(dir => {
    fs.mkdir(dir, err => {
      if (err && err.code != 'EEXIST') throw err
      // Delete created directory (or skipped) from controlledPath
      controlledPaths.splice(controlledPaths.indexOf(dir), 1)
      if (controlledPaths.length === 0) {
        return callback()
      }
    })
  })
}

// Usage
mkdirRecursive('./photos/recent', () => {
  console.log('Directories created succesfully!')
})

0

これmkdirpがnodejs の私の命令バージョンです。

function mkdirSyncP(location) {
    let normalizedPath = path.normalize(location);
    let parsedPathObj = path.parse(normalizedPath);
    let curDir = parsedPathObj.root;
    let folders = parsedPathObj.dir.split(path.sep);
    folders.push(parsedPathObj.base);
    for(let part of folders) {
        curDir = path.join(curDir, part);
        if (!fs.existsSync(curDir)) {
            fs.mkdirSync(curDir);
        }
    }
}

0

このアプローチはどうですか:

if (!fs.existsSync(pathToFile)) {
            var dirName = "";
            var filePathSplit = pathToFile.split('/');
            for (var index = 0; index < filePathSplit.length; index++) {
                dirName += filePathSplit[index]+'/';
                if (!fs.existsSync(dirName))
                    fs.mkdirSync(dirName);
            }
        }

これは相対パスで機能します。


0

mouneerのゼロ依存性の回答に基づいTypescriptて、モジュールとして、これは少し初心者にやさしいバリアントです。

import * as fs from 'fs';
import * as path from 'path';

/**
* Recursively creates directories until `targetDir` is valid.
* @param targetDir target directory path to be created recursively.
* @param isRelative is the provided `targetDir` a relative path?
*/
export function mkdirRecursiveSync(targetDir: string, isRelative = false) {
    const sep = path.sep;
    const initDir = path.isAbsolute(targetDir) ? sep : '';
    const baseDir = isRelative ? __dirname : '.';

    targetDir.split(sep).reduce((prevDirPath, dirToCreate) => {
        const curDirPathToCreate = path.resolve(baseDir, prevDirPath, dirToCreate);
        try {
            fs.mkdirSync(curDirPathToCreate);
        } catch (err) {
            if (err.code !== 'EEXIST') {
                throw err;
            }
            // caught EEXIST error if curDirPathToCreate already existed (not a problem for us).
        }

        return curDirPathToCreate; // becomes prevDirPath on next call to reduce
    }, initDir);
}

0

これと同じくらいきれいです:)

function makedir(fullpath) {
  let destination_split = fullpath.replace('/', '\\').split('\\')
  let path_builder = destination_split[0]
  $.each(destination_split, function (i, path_segment) {
    if (i < 1) return true
    path_builder += '\\' + path_segment
    if (!fs.existsSync(path_builder)) {
      fs.mkdirSync(path_builder)
    }
  })
}

0

fs.mkdirの再帰オプションに問題があったため、次の機能を実行しました。

  1. 最終的なターゲットディレクトリからルートの親まで、すべてのディレクトリのリストを作成します。
  2. mkdir関数が機能するために必要なディレクトリの新しいリストを作成します
  3. 最終を含む各ディレクトリを必要なものにします

    function createDirectoryIfNotExistsRecursive(dirname) {
        return new Promise((resolve, reject) => {
           const fs = require('fs');
    
           var slash = '/';
    
           // backward slashes for windows
           if(require('os').platform() === 'win32') {
              slash = '\\';
           }
           // initialize directories with final directory
           var directories_backwards = [dirname];
           var minimize_dir = dirname;
           while (minimize_dir = minimize_dir.substring(0, minimize_dir.lastIndexOf(slash))) {
              directories_backwards.push(minimize_dir);
           }
    
           var directories_needed = [];
    
           //stop on first directory found
           for(const d in directories_backwards) {
              if(!(fs.existsSync(directories_backwards[d]))) {
                 directories_needed.push(directories_backwards[d]);
              } else {
                 break;
              }
           }
    
           //no directories missing
           if(!directories_needed.length) {
              return resolve();
           }
    
           // make all directories in ascending order
           var directories_forwards = directories_needed.reverse();
    
           for(const d in directories_forwards) {
              fs.mkdirSync(directories_forwards[d]);
           }
    
           return resolve();
        });
     }

-1

ExecはWindowsで乱雑になる可能性があります。より「ノーディー」なソリューションがあります。基本的に、ディレクトリが存在するかどうかを確認し、子に飛び込む(存在する場合)、または作成するための再帰呼び出しがあります。子を作成し、終了時に関数を呼び出す関数を次に示します。

fs = require('fs');
makedirs = function(path, func) {
 var pth = path.replace(/['\\]+/g, '/');
 var els = pth.split('/');
 var all = "";
 (function insertOne() {
   var el = els.splice(0, 1)[0];
   if (!fs.existsSync(all + el)) {
    fs.mkdirSync(all + el);
   }
   all += el + "/";
   if (els.length == 0) {
    func();
   } else {
     insertOne();
   }
   })();

}


-1

このバージョンは、Windowsで正常に機能します。これは、両方/を理解し、path.sepWindowsでスラッシュが正常に機能するためです。絶対パスと相対パスをサポートします(相対パスprocess.cwd)。

/**
 * Creates a folder and if necessary, parent folders also. Returns true
 * if any folders were created. Understands both '/' and path.sep as 
 * path separators. Doesn't try to create folders that already exist,
 * which could cause a permissions error. Gracefully handles the race 
 * condition if two processes are creating a folder. Throws on error.
 * @param targetDir Name of folder to create
 */
export function mkdirSyncRecursive(targetDir) {
  if (!fs.existsSync(targetDir)) {
    for (var i = targetDir.length-2; i >= 0; i--) {
      if (targetDir.charAt(i) == '/' || targetDir.charAt(i) == path.sep) {
        mkdirSyncRecursive(targetDir.slice(0, i));
        break;
      }
    }
    try {
      fs.mkdirSync(targetDir);
      return true;
    } catch (err) {
      if (err.code !== 'EEXIST') throw err;
    }
  }
  return false;
}

Windowsを正しくサポートすることへの反対票はありましたか?他のOSでも動作することについて言及しましたか?
Qwertie 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.