
There are two similar but distinct modules that can be used to list files and directories in Node.js.
- fs.readdir (asynchronous)
- fs.readdirSync (synchronous) (this article)
fs.readdirSync can be used to list the files and directories in a directory. In this example, the files and directories in the /tmp directory will be stored in a list named "files_and_directories"
const fs = require("fs")
const base_directory = '/tmp'
const files_and_directories = fs.readdirSync(base_directory)
console.log(files_and_directories)
Be aware that if you are using a Node.js module instead of CommonJS, you will have "type": "module" in package.json. In this scenario, you will use import instead of require.
import fs from "fs"
Something like this should be returned.
[
'.hidden_file',
'.hidden_directory',
'my_directory',
'my.json',
'foo.txt',
'bar.txt'
]
fs.readdir is asynchronous where fs.readdirSync is not asynchronous. For example, let's say the /tmp/foo directory contains thousands of files and sub-directories and /tmp/bar only contains a few files. With fs.readdirSync (not asynchronous) the first block that gets all of the files and subdirectories in /tmp/foo must complete before moving onto the next block and gets the files in the /tmp/bar directory.
import fs from "fs"
fs.readdir('/tmp/foo', (err, files_and_directories) => {
if (err) {
console.error(err)
} else {
console.log(files_and_directories)
}
})
fs.readdir('/tmp/bar', (err, files_and_directories) => {
if (err) {
console.error(err)
} else {
console.log(files_and_directories)
}
})
The output from fs.readdirSync (not asynchronous) would be something like this, where the first list will be all of the files and sub-directories in /tmp/foo and the second list will be the files in /tmp/bar.
[
'0.txt', '1.txt', '10.txt', '11.txt', '12.txt', '13.txt',
'14.txt', '15.txt', '16.txt', '17.txt', '18.txt', '19.txt',
'2.txt', '20.txt', '21.txt', '22.txt', '23.txt', '24.txt',
'25.txt', '26.txt', '27.txt', '28.txt', '29.txt', '3.txt',
'30.txt', '31.txt', '32.txt', '33.txt', '34.txt', '35.txt',
'36.txt', '37.txt', '38.txt', '39.txt', '4.txt', '40.txt',
'41.txt', '42.txt', '43.txt', '44.txt', '45.txt', '46.txt',
'47.txt', '48.txt', '49.txt', '5.txt', '50.txt', '51.txt',
'52.txt', '53.txt', '54.txt', '55.txt', '56.txt', '57.txt',
'58.txt', '59.txt', '6.txt', '60.txt', '61.txt', '62.txt',
'63.txt', '64.txt', '65.txt', '66.txt', '67.txt', '68.txt',
'69.txt', '7.txt', '70.txt', '71.txt', '72.txt', '73.txt',
'74.txt', '75.txt', '76.txt', '77.txt', '78.txt', '79.txt',
'8.txt', '80.txt', '81.txt', '82.txt', '83.txt', '84.txt',
'85.txt', '86.txt', '87.txt', '88.txt', '89.txt', '9.txt',
'90.txt', '91.txt', '92.txt', '93.txt', '94.txt', '95.txt',
'96.txt', '97.txt', '98.txt', '99.txt',
... 9999 more items
]
[ 'hello.txt', 'world.txt' ]
On the other hand, fs.readdir (asynchronous) would probably first return the files in the /tmp/bar directory because it should take much less time for this block to complete and then the list of files and sub-directories in the /tmp/foo directory would come next.
[ 'hello.txt', 'world.txt' ]
[
'0.txt', '1.txt', '10.txt', '11.txt', '12.txt', '13.txt',
'14.txt', '15.txt', '16.txt', '17.txt', '18.txt', '19.txt',
'2.txt', '20.txt', '21.txt', '22.txt', '23.txt', '24.txt',
'25.txt', '26.txt', '27.txt', '28.txt', '29.txt', '3.txt',
'30.txt', '31.txt', '32.txt', '33.txt', '34.txt', '35.txt',
'36.txt', '37.txt', '38.txt', '39.txt', '4.txt', '40.txt',
'41.txt', '42.txt', '43.txt', '44.txt', '45.txt', '46.txt',
'47.txt', '48.txt', '49.txt', '5.txt', '50.txt', '51.txt',
'52.txt', '53.txt', '54.txt', '55.txt', '56.txt', '57.txt',
'58.txt', '59.txt', '6.txt', '60.txt', '61.txt', '62.txt',
'63.txt', '64.txt', '65.txt', '66.txt', '67.txt', '68.txt',
'69.txt', '7.txt', '70.txt', '71.txt', '72.txt', '73.txt',
'74.txt', '75.txt', '76.txt', '77.txt', '78.txt', '79.txt',
'8.txt', '80.txt', '81.txt', '82.txt', '83.txt', '84.txt',
'85.txt', '86.txt', '87.txt', '88.txt', '89.txt', '9.txt',
'90.txt', '91.txt', '92.txt', '93.txt', '94.txt', '95.txt',
'96.txt', '97.txt', '98.txt', '99.txt',
... 9999 more items
]
In Node.js version 20 and above, recursive: true can be used to do a recursive search for all files and directories at and below the base target directory.
import fs from "fs"
const base_directory = '/tmp'
const files_and_directories = fs.readdirSync(base_directory, {recursive: true})
console.log(files_and_directories)
forEach can be used to loop through the list.
import fs from "fs"
const base_directory = '/tmp'
const files_and_directories = fs.readdirSync(base_directory, {recursive: true}).forEach(item => {
console.log(item)
})
fs.lstatSync can be used to return the stats of each item to determine if the item . . .
- isFile()
- isDirectory()
- isSymbolicLink()
- isFIFO()
- isSocket()
- isCharacterDevice()
- isBlockDevice()
For example, this will only return files.
import fs from "fs"
const base_directory = '/tmp'
fs.readdirSync(base_directory, {recursive: true}).forEach(item => {
const full_path_to_item = base_directory + '/' + item
if (fs.lstatSync(full_path_to_item).isFile()) {
console.log(full_path_to_item)
}
})
Since files may be deleted in the midst of this block (such as with temp files) there is a possibility that something like "no such file or directory" could be returned so I'd include fs.existsSync to re-check if the file or directory still exists.
import fs from "fs"
const base_directory = '/tmp'
fs.readdirSync(base_directory, {recursive: true}).forEach(item => {
const full_path_to_item = base_directory + '/' + item
if (fs.exists(full_path_to_item)) {
if (fs.lstatSync(full_path_to_item).isFile()) {
console.log(full_path_to_item)
}
}
})
sort can be used to sort the results.
import fs from "fs"
const base_directory = '/tmp'
const files = fs.readdirSync(base_directory, {recursive: true}).filter(item => fs.lstatSync(base_directory + '/' + item).isFile()).sort()
files.forEach(item => {
console.log(item)
})
RegExp can be used to only return files that match a regular expression. In this example, only files ending in .txt will be appended to the txt_files list.
import fs from "fs"
const base_directory = '/tmp'
const re = new RegExp(/\.txt$/)
const txt_files = []
const files = fs.readdirSync(base_directory, {recursive: true}).filter(item => fs.lstatSync(base_directory + '/' + item).isFile()).sort()
files.forEach(item => {
if (re.test(item)) {
txt_files.push(item)
}
})
txt_files.forEach(item => {
console.log(item)
})
Did you find this article helpful?
If so, consider buying me a coffee over at