ThomasReggi
ThomasReggi

Reputation: 59525

Node.js check if path is file or directory

I can't seem to get any search results that explain how to do this.

All I want to do is be able to know if a given path is a file or a directory (folder).

Upvotes: 602

Views: 438442

Answers (9)

Chen Peleg
Chen Peleg

Reputation: 2124

Node 20+ ESM

This is an ESM version of @Jason's answer

import { lstatSync } from 'node:fs'; 

export const isDirectory = (path) => lstatSync(path) ? lstatSync(path).isDirectory() : false;

In case there is no directory or file at that path, the lstatSync will throw an error no such file or directory

Upvotes: 0

kungfooman
kungfooman

Reputation: 4892

Seriously, question exists five years and no nice facade?

import {lstatSync} from 'fs';
/**
 * @param {string} path - The path.
 * @returns {boolean} Whether path is a directory, otherwise always false.
 */
function isDir(path) {
  try {
    const stat = lstatSync(path);
    return stat.isDirectory();
  } catch (e) {
    // lstatSync throws an error if path doesn't exist
    return false;
  }
}
console.log("Is /tmp      a dir?", isDir("/tmp")      ? 'Yes!' : 'No!');
console.log("Is /root     a dir?", isDir("/root")     ? 'Yes!' : 'No!');
console.log("Is /dev      a dir?", isDir("/dev")      ? 'Yes!' : 'No!');
console.log("Is /dev/null a dir?", isDir("/dev/null") ? 'Yes!' : 'No!');

Outputs on a unix-like system:

Is /tmp      a dir? Yes!
Is /root     a dir? Yes!
Is /dev      a dir? Yes!
Is /dev/null a dir? No!

Upvotes: 35

Zdenek F
Zdenek F

Reputation: 1919

If you need this when iterating over a directory (Because that's how I've found this question):

Since Node 10.10+, fs.readdir has a withFileTypes option which makes it return directory entry fs.Dirent instead of strings. Directory entries has a name property, and useful methods such as isDirectory or isFile, so you don't need to call fs.lstat explicitly.

import { promises as fs } from 'fs';

// ./my-dir has two subdirectories: dir-a, and dir-b

const dirEntries = await fs.readdir('./my-dir', { withFileTypes: true });

// let's filter all directories in ./my-dir

const onlyDirs = dirEntries.filter(de => de.isDirectory()).map(de => de.name);

// onlyDirs is now [ 'dir-a', 'dir-b' ]

Upvotes: 28

user3038477
user3038477

Reputation: 1

Function that returns type

I like coffee

type: (uri)-> (fina) ->
  fs.lstat uri, (erro,stats) ->
    console.log {erro} if erro
    fina(
      stats.isDirectory() and "directory" or
      stats.isFile() and "document" or
      stats.isSymbolicLink() and "link" or
      stats.isSocket() and "socket" or
      stats.isBlockDevice() and "block" or
      stats.isCharacterDevice() and "character" or
      stats.isFIFO() and "fifo"
    )

usage:

dozo.type("<path>") (type) ->
  console.log "type is #{type}"

Upvotes: -2

Jason Sperske
Jason Sperske

Reputation: 30446

The following should tell you. From the docs:

fs.lstatSync(path_string).isDirectory() 

Objects returned from fs.stat() and fs.lstat() are of this type.

stats.isFile()
stats.isDirectory()
stats.isBlockDevice()
stats.isCharacterDevice()
stats.isSymbolicLink() // (only valid with fs.lstat())
stats.isFIFO()
stats.isSocket()

NOTE:

The above solution will throw an Error if; for ex, the file or directory doesn't exist.

If you want a true or false approach, try fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory(); as mentioned by Joseph in the comments below.

Upvotes: 925

vdegenne
vdegenne

Reputation: 13298

Here's a function that I use. Nobody is making use of promisify and await/async feature in this post so I thought I would share.

const promisify = require('util').promisify;
const lstat = promisify(require('fs').lstat);

async function isDirectory (path) {
  try {
    return (await lstat(path)).isDirectory();
  }
  catch (e) {
    return false;
  }
}

Note : I don't use require('fs').promises; because it has been experimental for one year now, better not rely on it.

Upvotes: 3

Marcos Casagrande
Marcos Casagrande

Reputation: 40444

Update: Node.Js >= 10

We can use the new fs.promises API

const fs = require('fs').promises;

(async() => {
    const stat = await fs.lstat('test.txt');
    console.log(stat.isFile());
})().catch(console.error)

Any Node.Js version

Here's how you would detect if a path is a file or a directory asynchronously, which is the recommended approach in node. using fs.lstat

const fs = require("fs");

let path = "/path/to/something";

fs.lstat(path, (err, stats) => {

    if(err)
        return console.log(err); //Handle error

    console.log(`Is file: ${stats.isFile()}`);
    console.log(`Is directory: ${stats.isDirectory()}`);
    console.log(`Is symbolic link: ${stats.isSymbolicLink()}`);
    console.log(`Is FIFO: ${stats.isFIFO()}`);
    console.log(`Is socket: ${stats.isSocket()}`);
    console.log(`Is character device: ${stats.isCharacterDevice()}`);
    console.log(`Is block device: ${stats.isBlockDevice()}`);
});

Note when using the synchronous API:

When using the synchronous form any exceptions are immediately thrown. You can use try/catch to handle exceptions or allow them to bubble up.

try{
     fs.lstatSync("/some/path").isDirectory()
}catch(e){
   // Handle error
   if(e.code == 'ENOENT'){
     //no such file or directory
     //do something
   }else {
     //do something else
   }
}

Upvotes: 100

cndw
cndw

Reputation: 181

Depending on your needs, you can probably rely on node's path module.

You may not be able to hit the filesystem (e.g. the file hasn't been created yet) and tbh you probably want to avoid hitting the filesystem unless you really need the extra validation. If you can make the assumption that what you are checking for follows .<extname> format, just look at the name.

Obviously if you are looking for a file without an extname you will need to hit the filesystem to be sure. But keep it simple until you need more complicated.

const path = require('path');

function isFile(pathItem) {
  return !!path.extname(pathItem);
}

Upvotes: 14

TamusJRoyce
TamusJRoyce

Reputation: 834

The answers above check if a filesystem contains a path that is a file or directory. But it doesn't identify if a given path alone is a file or directory.

The answer is to identify directory-based paths using "/." like --> "/c/dos/run/." <-- trailing period.

Like a path of a directory or file that has not been written yet. Or a path from a different computer. Or a path where both a file and directory of the same name exists.

// /tmp/
// |- dozen.path
// |- dozen.path/.
//    |- eggs.txt
//
// "/tmp/dozen.path" !== "/tmp/dozen.path/"
//
// Very few fs allow this. But still. Don't trust the filesystem alone!

// Converts the non-standard "path-ends-in-slash" to the standard "path-is-identified-by current "." or previous ".." directory symbol.
function tryGetPath(pathItem) {
    const isPosix = pathItem.includes("/");
    if ((isPosix && pathItem.endsWith("/")) ||
        (!isPosix && pathItem.endsWith("\\"))) {
        pathItem = pathItem + ".";
    }
    return pathItem;
}
// If a path ends with a current directory identifier, it is a path! /c/dos/run/. and c:\dos\run\.
function isDirectory(pathItem) {
    const isPosix = pathItem.includes("/");
    if (pathItem === "." || pathItem ==- "..") {
        pathItem = (isPosix ? "./" : ".\\") + pathItem;
    }
    return (isPosix ? pathItem.endsWith("/.") || pathItem.endsWith("/..") : pathItem.endsWith("\\.") || pathItem.endsWith("\\.."));
} 
// If a path is not a directory, and it isn't empty, it must be a file
function isFile(pathItem) {
    if (pathItem === "") {
        return false;
    }
    return !isDirectory(pathItem);
}

Node version: v11.10.0 - Feb 2019

Last thought: Why even hit the filesystem?

Upvotes: 1

Related Questions