Table of Contents

lua_fs - std::filesystem for Lua

lua_fs is a Lua loadable shared library that exposes the C++17 std::filesystem library through sol2. Paths can be passed as ordinary Lua strings or as fs.path objects. Filesystem failures raise a Lua error containing the operating-system error message.

API Reference

All examples assume:

local fs = require("lua_fs")

Paths may be Lua strings or fs.path objects. A function that encounters an operating-system error raises a Lua error. Boolean query functions return false for a missing path unless the underlying operation itself fails.

fs.path

fs.path(value) constructs a path object. value is optional for an empty path or can be a string/path value. Path objects are useful when several lexical operations are chained; filesystem functions accept them anywhere a string is accepted.

local path = fs.path("logs/app.2026.txt")
print(path:parent_path(), path:stem(), path:extension())

Path methods

Method Description and result Example
path:string() Returns the platform-native path representation as a string. print(path:string())
path:generic_string() Returns a portable representation using / separators. print(path:generic_string())
path:filename() Returns the final path component, or an empty string. local name = path:filename()
path:parent_path() Returns the lexical parent path. local folder = path:parent_path()
path:root_name() Returns a root name such as C: on Windows. print(path:root_name())
path:root_directory() Returns the root directory component, such as / or \\. print(path:root_directory())
path:relative_path() Returns the path with its root components removed. print(path:relative_path())
path:stem() Returns the filename without its final extension. print(path:stem())
path:extension() Returns the final extension, including its dot, or an empty string. assert(path:extension() == ".txt")
path:lexically_normal() Returns a normalized path without accessing the filesystem. local clean = path:lexically_normal()
path:lexically_relative(base) Returns the lexical path from base to path; it can be empty when no relative form exists. print(path:lexically_relative("logs"))
path:has_root_name() Returns whether the path has a root name. if path:has_root_name() then ... end
path:has_root_directory() Returns whether the path has a root directory. print(path:has_root_directory())
path:has_parent_path() Returns whether a parent component exists. print(path:has_parent_path())
path:has_filename() Returns whether a filename component exists. print(path:has_filename())
path:has_stem() Returns whether a stem component exists. print(path:has_stem())
path:has_extension() Returns whether an extension component exists. print(path:has_extension())
path:is_absolute() Returns whether the path is absolute according to the host platform. print(path:is_absolute())
path:is_relative() Returns whether the path is not absolute. print(path:is_relative())
path:empty() Returns whether the path contains no characters. assert(not path:empty())
path:append(value) Appends a path component using the platform separator and returns the same path object. path:append("archive")
path:concat(value) Concatenates text without inserting a separator and returns the same path object. path:concat(".bak")

Path and environment functions

Function Description and result Example
fs.absolute(path) Returns an absolute path without requiring the target to exist. print(fs.absolute("data/input.txt"))
fs.canonical(path) Returns the fully resolved absolute path. Every component must exist. print(fs.canonical("data"))
fs.weakly_canonical(path) Resolves existing components and permits a non-existing final portion. print(fs.weakly_canonical("data/new.txt"))
fs.relative(path, base) Returns the path from base to path, using the current directory for omitted filesystem context. print(fs.relative("data/a.txt", "data"))
fs.proximate(path, base) Returns relative(path, base), or an absolute path when a relative result is not possible. print(fs.proximate("data/a.txt", "data"))
fs.current_path() Returns the process working directory. print(fs.current_path())
fs.set_current_path(path) Changes the process working directory. Returns no value. fs.set_current_path("work")
fs.temp_directory_path() Returns the operating system temporary directory. local temp = fs.temp_directory_path()
fs.directory(path) Returns a 1-based Lua array of immediate child paths.
for _, p in ipairs(fs.directory("data")) do
   print(p)
end
fs.recursive_directory(path) Returns a 1-based Lua array containing all descendants recursively.
for _, p in ipairs(fs.recursive_directory("data")) do
   print(p)
end

Queries and metadata

Function Description and result Example
fs.exists(path) Returns true when the path exists, including a symlink target check. if fs.exists("config.lua") then ... end
fs.equivalent(left, right) Returns whether two existing paths refer to the same filesystem object. print(fs.equivalent("a", "b"))
fs.status(path) Returns metadata after following symlinks. local info = fs.status("file.txt")
fs.symlink_status(path) Returns metadata for the link itself without following a symlink. local info = fs.symlink_status("link")
fs.is_block_file(path) Returns whether the path is a block device. print(fs.is_block_file(path))
fs.is_character_file(path) Returns whether the path is a character device. print(fs.is_character_file(path))
fs.is_fifo(path) Returns whether the path is a named pipe/FIFO. print(fs.is_fifo(path))
fs.is_other(path) Returns whether the path exists but is not a regular file, directory, or symlink. print(fs.is_other(path))
fs.is_socket(path) Returns whether the path is a socket where the platform supports that type. print(fs.is_socket(path))
fs.is_symlink(path) Returns whether the path is a symbolic link. print(fs.is_symlink(path))
fs.is_regular_file(path) Returns whether the path is a regular file. assert(fs.is_regular_file("file.txt"))
fs.is_directory(path) Returns whether the path is a directory. if fs.is_directory(path) then ... end
fs.file_size(path) Returns the file size in bytes. The path must refer to a regular file. print(fs.file_size("file.txt"))
fs.hard_link_count(path) Returns the number of hard links to the filesystem object. print(fs.hard_link_count("file.txt"))
fs.last_write_time(path) Returns the last modification time as Unix seconds. print(os.date("%F", fs.last_write_time(path)))
fs.set_last_write_time(path, seconds) Sets the last modification time from Unix seconds. Returns no value. fs.set_last_write_time(path, os.time())
fs.space(path) Returns { capacity, free, available }, measured in bytes, for the filesystem containing path. print(fs.space(".").available)

status and symlink_status return a table with these fields:

local info = fs.status("file.txt")
print(info.type, info.permissions)
print(info.exists, info.is_regular_file, info.is_directory, info.is_symlink)

type is the numeric C++ file-type value and permissions is the numeric permission bitmask. Use the fs.perms constants below when interpreting or changing that mask.

Filesystem mutations

Function Description and result Example
fs.create_directory(path) Creates one directory. Returns true if created and false if it already existed. fs.create_directory("output")
fs.create_directories(path) Creates a directory and any missing parents. Returns true if anything was created. fs.create_directories("output/images")
fs.remove(path) Removes one file, empty directory, or symlink. Returns true if removed. fs.remove("output/old.txt")
fs.remove_all(path) Recursively removes a path and its contents. Returns the number of removed filesystem objects. print(fs.remove_all("output"))
fs.rename(from, to) Renames or moves a filesystem object. Returns no value. fs.rename("draft.txt", "final.txt")
fs.copy(from, to, options) Copies a file or directory according to fs.copy_options. Returns no value. fs.copy("assets", "backup", fs.copy_options.recursive)
fs.copy_file(from, to, options) Copies one file and returns true when the copy succeeds. fs.copy_file("a.txt", "b.txt", fs.copy_options.overwrite_existing)
fs.resize_file(path, size) Changes a regular file to size bytes. Returns no value. fs.resize_file("cache.bin", 0)
fs.create_hard_link(existing, link) Creates a hard link named link to existing. Returns no value. fs.create_hard_link("a.txt", "a-link.txt")
fs.create_symlink(existing, link) Creates a symbolic link. On Windows, the process may need symlink privileges. Returns no value. fs.create_symlink("a.txt", "a-link.txt")
fs.create_directory_symlink(existing, link) Creates a symbolic link specifically for a directory. Returns no value. fs.create_directory_symlink("assets", "assets-link")
fs.permissions(path, bits, options) Replaces, adds, or removes permission bits. Use fs.perms and fs.perm_options. Returns no value. fs.permissions("private.txt", fs.perms.owner_read, fs.perm_options.replace)

Option and permission constants

fs.copy_options contains the flags accepted by copy and copy_file:

Constant Meaning
none Use default copy behavior.
skip_existing Keep an existing destination.
overwrite_existing Replace an existing destination.
update_existing Replace only when the source is newer.
recursive Copy directory contents recursively.
copy_symlinks Copy symlinks as symlinks.
skip_symlinks Do not copy symlinks.
directories_only Copy directory structure without regular files.
create_symlinks Create symlinks instead of copies.
create_hard_links Create hard links instead of copies.

fs.perms contains the standard permission bits: owner_read, owner_write, owner_exec, group_read, group_write, group_exec, others_read, others_write, others_exec, all, set_uid, set_gid, and sticky_bit.

fs.perm_options contains replace, add, and remove:

fs.permissions("script.sh", fs.perms.owner_exec, fs.perm_options.add)
fs.permissions("script.sh", fs.perms.others_write, fs.perm_options.remove)