Düğümün fs.mkdirSync ile tam yol nasıl oluşturulur?


161

Eğer yoksa tam bir yol oluşturmaya çalışıyorum.

Kod şöyle görünür:

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

Bu kod, yalnızca bir alt dizin ('dir1' gibi bir newDest) olduğu sürece harika çalışır, ancak ('dir1 / dir2') gibi bir dizin yolu olduğunda Hata: ENOENT ile başarısız olur , böyle bir dosya veya dizin yok

Gerektiği kadar kod satırı ile tam yolu oluşturmak istiyorum.

Ben fs üzerinde özyinelemeli bir seçenek olduğunu okudum ve böyle denedim

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

Varolmayan bir dizin oluşturmak için bu kadar basit olmalı gibi hissediyorum. Bir şey eksik mi yoksa yolu ayrıştırıp her dizini kontrol edip zaten yoksa oluşturmam gerekiyor mu?

Düğümde oldukça yeniyim. Belki FS'nin eski bir sürümünü kullanıyorum?



4
@AndyRay Bu StackOverflow soru şimdi bu soru için google'da en iyi sonuç, bu komik çünkü bu özyineleme anlamına gelir ....
Matt

1
Bu, Düğümün eski sürümlerinde bir sorundu, Düğüm 12+ sürümüne güncelleme sorunu çözdü
MrJomp

Yanıtlar:


48

Bir seçenek shelljs modülünü kullanmaktır

npm install shelljs

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

Bu sayfadan:

Mevcut seçenekler:

p: tam yol (gerekirse ara yönler oluşturur)

Diğerlerinin de belirttiği gibi, daha odaklanmış başka modüller de var. Ancak, mkdirp dışında, tonlarca başka yararlı kabuk işlemi var (ki, grep vb ...) ve pencereler ve * nix üzerinde çalışıyor


2
Teşekkürler! Ben exec kullanarak sona erdi (zaten bunu kullanıyordum) ve bir cazibe gibi çalıştı. var exec = zorunlu ('child_process'). exec; var command = "mkdir -p '" + newDest + "'"; var options = {}; var after = function (hata, stdout, stderr) {console.log ('hata', hata); console.log ('stdout', stdout); console.log ('stderr', stderr); } exec (komut, seçenekler, sonra);
David Silva Smith

24
Bu seçenek, komut satırı mkdir örneği (yani Linux-y olmayan ana bilgisayarlar) içermeyen node.js platformlarında kırılabilir, bu nedenle önemli değilse taşınabilir değildir.
cshotton

1
@cshotton - açıklamaya veya cevaba atıfta bulunuyor musunuz? shelljs pencerelerde bile çalışır. exec mkdir -p (yorum) elbette değil.
bryanmac

Bu güzel işlevi Promise veya istediğiniz geri arama ile kullanabilirsiniz .
Илья Зеленько

1
bu bir çözüm değil, bu çözümün bir alternatifidir. bağlam: pics.onsizzle.com/…
Nika Kasradze

413

Düzenle

NodeJS versiyonu 10.12.0bir yerel destek hem de eklemiştir mkdirve mkdirSyncbirlikte yinelemeli bir dizin oluşturmak için recursive: trueaşağıdaki gibi seçenek:

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

Ve eğer isterseniz fs Promises API, yazabilirsiniz

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

Orijinal Yanıt

Varsa dizinleri özyinelemeli olarak oluşturun! ( Sıfır bağımlılık )

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);
}

kullanım

// 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');

gösteri

Dene!

açıklamalar

  • [GÜNCELLEME] gibi Bu çözüm kolları platforma özel hataları EISDIR, Mac ve EPERMve EACCESWindows için. @PediT., @JohnQ, @ deed02392, @robyoder ve @Almenon'un tüm raporlama yorumları sayesinde.
  • Bu çözelti, iki işleme göre ve mutlak yolları. @John yorumu için teşekkürler.
  • Göreli yollar durumunda, geçerli çalışma dizininde hedef dizinler oluşturulur (çözülür). Geçerli komut dosyası dizinine göre bunları çözmek için iletin {isRelativeToScript: true}.
  • Platformlar arası sorunlardan kaçınmak için yalnızca birleştirme değil path.sepve kullanma .path.resolve()/
  • Kullanılması fs.mkdirSyncve hatayı işleme try/catchyarış koşulları ele atılırsa: Başka bir işlem için çağrılar arasında dosya ekleyebilir fs.existsSync()ve fs.mkdirSync()bir duruma neden olur.
    • Bunu başarmanın bir diğer yolu da bir dosyanın var olup olmadığını kontrol etmek ve daha sonra onu oluşturmak olabilir if (!fs.existsSync(curDir) fs.mkdirSync(curDir);. Ancak bu, kodu yarış koşullarına karşı savunmasız bırakan bir anti-modeldir. @GershomMaes dizin varlığı kontrolü hakkında yorum sayesinde.
  • Gerektirir Düğüm v6 destek strüktür kırıcı ve daha yeni. (Bu çözümü eski Düğüm sürümleriyle uygulamada sorun yaşıyorsanız, bana bir yorum bırakın)

7
Ek bir kütüphane veya yaklaşım gerektirmeyen kolay, özyinelemeli yanıt için oy verin!
MikingTheViking

1
Eksik ifadeler eksik: const fs = requir ('fs'); sabit yol = zorunlu ('yol');
Christopher Bull

1
@ChristopherBull, sadece mantığa odaklanmak için kasıtlı olarak eklenmedi, ama yine de onları ekledim. Teşekkürler;)
Mouneer

1
12 satır katı kod, sıfır bağımlılık, her seferinde alacağım.
moodboom

1
Mac OS X 10.12.6'da @Mouneer, mutlak bir yoldan geçtikten sonra "/" oluşturmaya çalışırken atılan hata "EISDIR" (Hata: EISDIR: bir dizinde geçersiz işlem, mkdir '/'). Muhtemelen dir varlığını kontrol etmek hala gitmek için en iyi çapraz platform yolu (yavaş olacağını kabul).
John Q

78

Daha sağlam bir cevap mkdirp kullanımı kullanmaktır .

var mkdirp = require('mkdirp');

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

Ardından, dosyayı tam yola şu yolla yazmaya devam edin:

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

Tüm kütüphaneyi değil, sadece ihtiyacınız olanı içe aktardığınız için bu cevabı tercih edin
Juan Mendes

1
Popülist rozeti için tebrikler ;-)
janos

1
Teşekkürler. En iyi yöntemdir.
Stepan Rafael


48

fs-extra, yerel fs modülünde bulunmayan dosya sistemi yöntemlerini ekler. Bu fs yerine bir damla.

Yüklemek fs-extra

$ npm install --save fs-extra

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

Senkronizasyon ve zaman uyumsuz seçenekler var.

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


5
Bu en iyi cevap! Çoğumuz zaten zaten app fs-extra var.
sayfa

memfsÜnite testi için kullanım imkanı sunması harika olurdu . Yapmaz
schnatterer

31

Reduce kullanarak her bir yolun var olup olmadığını doğrulayabilir ve gerektiğinde oluşturabiliriz, bu şekilde takip etmenin daha kolay olduğunu düşünüyorum. Düzenlendi, teşekkürler @Arvin, platforma özgü uygun yol segment ayırıcısını elde etmek için path.sep kullanmalıyız.

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
Bir cevap verirken cevabınızın NEDEN olduğu hakkında bazı açıklamalar yapmak tercih edilir .
Stephen Rauch

Üzgünüm, haklısın, bence bu şekilde daha temiz ve takip edilmesi daha kolay
josebui

4
@josebui Çevreye özgü sorunları önlemek için eğik çizgi (/) yerine "path.sep" kullanmanın daha iyi olduğunu düşünüyorum.
Arvin

iyi bir çözüm, çünkü diğer cevaplar gibi düğüm gerektirmez> = 10
Karim

29

Bu özellik, 10.12.0 sürümünde node.js'ye eklenmiştir, bu nedenle çağrının {recursive: true}ikinci argümanı olarak bir seçenek iletmek kadar kolaydır fs.mkdir(). Resmi belgelerdeki örneğe bakın .

Harici modüllere veya kendi uygulamanıza gerek yoktur.



1
Dizin mevcut olduğunda hata atar ve durur. Bir try catch bloğu kullanın, var olmayan diğer klasörleri oluşturmaya devam edebilir.
Choco Li

1
Bu kabul edilen cevap olmalı. Dizin zaten varsa atmaz ve fs.promises.mkdir aracılığıyla async / await ile kullanılabilir.
Zengin Apodaca

7

Bu eski bir soru olduğunu biliyorum, ama nodejs v10.12.0 şimdi recursiveseçeneği true olarak ayarlanmış bu doğal olarak destekler . 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 örneği (ekstra bağımlılık ve hata işleme yok)

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

Yoldaki klasörlerin var olup olmadığını tekrar tekrar kontrol edebilir ve mevcut olup olmadıklarını kontrol ederken klasörü oluşturabilirsiniz. ( HARİCİ KÜTÜPHANE YOK )

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

Bir sonraki işlevi kullanabilirsiniz

const recursiveUpload = (yol: dize) => {const yolları = path.split ("/")

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

  fs.mkdirSync(fullPath)

  return fullPath
}

Peki ne yapar:

  1. pathsDeğişken yarat , burada her yolu kendi başına bir dizi elemanı olarak saklar.
  2. Dizideki her öğenin sonuna "/" ekler.
  3. Döngü için yapar:
    1. Dizinlerin 0'dan geçerli yinelemeye kadar olan dizi öğelerinin birleşmesinden bir dizin oluşturur. Temel olarak, özyinelemeli.

Umarım yardımcı olur!

Bu arada, v10.12.0 Düğümünde, yinelemeli yol oluşturmayı ek argüman olarak vererek kullanabilirsiniz.

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

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


1

Çok fazla cevap var, ancak yolu tekrarlayarak ve ardından soldan sağa tekrar oluşturarak çalışan özyineleme olmadan bir çözüm

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 ile Linux uyumluluğu hakkında endişe duyanlar için, yukarıdaki eğik çizgiyi yukarıdaki her iki durumda da çift ters eğik çizgi ile değiştirin, ancak TBH, windows komut satırı değil, fs düğümünden bahsediyoruz ve birincisi oldukça affedicidir ve yukarıdaki kod sadece üzerinde çalışacaktır Windows ve daha eksiksiz bir çözüm çapraz platformdur.


Windows dosyaları eğik çizgi değil ters eğik çizgi ile işlenir. Kodunuz burada çalışmaz. C: \ data \ test ...
DDD

Düzenlendi, ancak yorumunuzu doğrulamanızı öneririz. Düğümde aşağıdakileri deneyin ve ne olduğunu görün var fs = requir ('fs') fs.mkdirSync ('test') fs.mkdirSync ('test \\ test1') fs.mkdirSync ('test / test2')
Hamiora

Ne diyorsan .., daha iyi kod yazmayı öğrenene kadar oylamam hala devam ediyor.
DDD

Haha. Tamam, daha iyi kod yazmayı öğrenmek için çok çalışacağım. BTW, OP de dahil olmak üzere yukarıdaki cevapların çoğunda eğik çizgi kullanır. Trol etmeyi bırakmanı önerin.
Hamiora

1
path.sepbenim için / veya \\ olarak geliyor. path.delimiterolduğu veya ;.
Josh Anderson Slate

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

Yinelenen dizinler oluşturmanın zaman uyumsuz bir yolu:

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

İşte benim mkdirpnodejs için zorunluluk versiyonum .

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

Bu yaklaşıma ne dersiniz:

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);
            }
        }

Bu göreli yol için çalışır.


0

Mouneer'in sıfır bağımlılık cevabına dayanarak, Typescriptmodül olarak biraz daha başlangıç ​​dostu bir varyant:

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

Bu kadar temiz :)

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

Ben fs.mkdir özyinelemeli seçeneği ile ilgili sorunlar vardı, bu yüzden aşağıdakileri yapan bir işlev yaptı:

  1. Son hedef dir ile başlayıp kök üst öğeye kadar tüm dizinlerin bir listesini oluşturur.
  2. Mkdir işlevinin çalışması için gerekli dizinlerin yeni bir listesini oluşturur
  3. Son dizini de içeren her dizini gerekli kılar

    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 pencerelerde dağınık olabilir. Daha "nodie" çözümü var. Temel olarak, bir dizinin var olup olmadığını görmek ve yinelemeli bir çağrınız vardır ve alt öğeye (varsa) dalın veya oluşturun. Tamamlandığında çocukları yaratacak ve bir işlevi çağıracak bir işlev şunlardır:

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

Bu sürüm, Windows'ta üst yanıttan daha iyi çalışır, çünkü her ikisini de anlar /ve path.sepböylece eğik çizgiler Windows'ta olması gerektiği gibi çalışır. Mutlak ve göreli yolları destekler 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;
}

Aşağı doğru destek Windows'u doğru bir şekilde destekledi mi? Diğer işletim sistemlerinde de çalıştığından bahsetmiş miydim?
Qwertie
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.