hs.fs
ModuleModule for filesystem operations.
hs.fs provides a comprehensive set of filesystem operations covering file
I/O, directory management, path manipulation, metadata access, symbolic
links, Finder tags, and macOS-specific features like file bookmarks and
Uniform Type Identifiers.
It replaces both Hammerspoon v1's hs.fs module and the functionality that
was previously available through Lua's built-in io and file modules.
Reading and writing files
const contents = hs.fs.read("/etc/hosts"); // entire file
const chunk = hs.fs.read("/etc/hosts", 100, 50); // 50 bytes from offset 100
hs.fs.readLines("/etc/hosts", function(line) {
console.log(line);
return true; // return false to stop early
});
hs.fs.write("/tmp/hello.txt", "Hello, world!\n");
hs.fs.append("/tmp/hello.txt", "More content\n");
Directory operations
hs.fs.mkdir("~/Projects/new-thing");
const files = hs.fs.list("~/Documents");
const all = hs.fs.listRecursive("~/Documents");
Path utilities
const abs = hs.fs.pathToAbsolute("~/Library");
const tmp = hs.fs.temporaryDirectory();
const home = hs.fs.homeDirectory();
Metadata
const info = hs.fs.attributes("/etc/hosts");
// { size: 1234, type: "file", permissions: 420,
// ownerID: 0, groupID: 0,
// creationDate: 1700000000.0, modificationDate: 1700001000.0 }
Properties
This module has no properties.
Methods
hs.fs.read(path, offset, length) -> string
Read part or all of a file as a UTF-8 string.
Declaration
hs.fs.read(path, offset, length) -> string
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to the file. `~` is expanded. |
| offset | number | Byte offset to start reading from. Pass `0` (or omit) to read from the beginning. |
| length | number | Maximum number of bytes to read. Pass `0` (or omit) to read to the end of the file. |
Returns
string
The file contents as a UTF-8 string, or `null` if the file cannot be read.
Example
const all = hs.fs.read("/etc/hosts") // entire file
const chunk = hs.fs.read("/etc/hosts", 100, 50) // 50 bytes starting at byte 100
hs.fs.readLines(path, callback) -> boolean
Read a file line-by-line, invoking a callback for each line.
Lines are delivered with newline characters stripped. Both `\n` and `\r\n` line endings are handled.
Declaration
hs.fs.readLines(path, callback) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to the file. `~` is expanded. |
| callback | JSValue | Called once per line with the line text. Return `true` to continue reading, or `false` to stop early. |
Returns
boolean
`true` if the file was read successfully (including early stops requested by the callback), or `false` if the file could not be opened.
Example
hs.fs.readLines("/etc/hosts", (line) => {
if (line.startsWith("#")) return true
console.log(line)
return true
})
hs.fs.write(path, content, inPlace) -> boolean
Write a UTF-8 string to a file, creating it or overwriting any existing content.
Intermediate directories are not created automatically; use `mkdir` first if needed.
Declaration
hs.fs.write(path, content, inPlace) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to the file. `~` is expanded. |
| content | string | String to write. |
| inPlace | boolean | Whether to write the file in-place or atomically. Defaults to atomically (false). |
Returns
boolean
`true` on success, `false` on failure.
Example
hs.fs.write("/tmp/hello.txt", "Hello, world!\n")
hs.fs.append(path, content) -> boolean
Append a UTF-8 string to a file, creating it if it does not exist.
Declaration
hs.fs.append(path, content) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to the file. `~` is expanded. |
| content | string | String to append. |
Returns
boolean
`true` on success, `false` on failure.
Example
hs.fs.append("/tmp/log.txt", "another line\n")
hs.fs.exists(path) -> boolean
Determine if a filesystem object exists at the given path
Unlike `isFile` and `isDirectory`, this follows symlinks.
Declaration
hs.fs.exists(path) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to check. `~` is expanded. |
Returns
boolean
`true` if any filesystem entry (file, directory, symlink, etc.) exists at the path.
Example
if (hs.fs.exists("/tmp/file.txt")) console.log("exists")
hs.fs.isFile(path) -> boolean
Determine if a file exists at the given path
This does **not** follow symlinks; a symlink pointing at a file returns `false`.
Declaration
hs.fs.isFile(path) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to check. `~` is expanded. |
Returns
boolean
`true` if a regular file (not a directory or symlink) exists at the path.
Example
console.log(hs.fs.isFile("/etc/hosts"))
hs.fs.isDirectory(path) -> boolean
Determine if a directory exists at the given path
This does **not** follow symlinks; a symlink pointing at a directory returns `false`.
Declaration
hs.fs.isDirectory(path) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to check. `~` is expanded. |
Returns
boolean
`true` if a directory exists at the path.
Example
console.log(hs.fs.isDirectory("/tmp"))
hs.fs.isSymlink(path) -> boolean
Determine if a symlink exists at the given path
Declaration
hs.fs.isSymlink(path) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to check. `~` is expanded. |
Returns
boolean
`true` if the path is a symbolic link.
Example
console.log(hs.fs.isSymlink("/var"))
hs.fs.isReadable(path) -> boolean
Determine if a given filesystem path is readable
Declaration
hs.fs.isReadable(path) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to check. `~` is expanded. |
Returns
boolean
`true` if the current process can read the file or directory at the path.
Example
console.log(hs.fs.isReadable("/etc/hosts"))
hs.fs.isWritable(path) -> boolean
Determine if a given filesystem path is writable
Declaration
hs.fs.isWritable(path) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to check. `~` is expanded. |
Returns
boolean
`true` if the current process can write to the file or directory at the path.
Example
console.log(hs.fs.isWritable("/tmp"))
hs.fs.copy(source, destination) -> boolean
Copy a file or directory to a new location.
The destination must not already exist. If `source` is a directory, its
entire contents are copied recursively.
Declaration
hs.fs.copy(source, destination) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| source | string | Path to the existing file or directory. `~` is expanded. |
| destination | string | Path for the copy. `~` is expanded. |
Returns
boolean
`true` on success, `false` on failure.
Example
hs.fs.copy("/tmp/a.txt", "/tmp/b.txt")
hs.fs.move(source, destination) -> boolean
Move (rename) a file or directory.
The destination must not already exist.
Declaration
hs.fs.move(source, destination) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| source | string | Path to the existing file or directory. `~` is expanded. |
| destination | string | New path. `~` is expanded. |
Returns
boolean
`true` on success, `false` on failure.
Example
hs.fs.move("/tmp/old.txt", "/tmp/new.txt")
hs.fs.deletePath(path) -> boolean
Delete a file or directory at the given path.
Directories are removed recursively. To remove only an empty directory,
use `rmdir` instead.
Declaration
hs.fs.deletePath(path) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to delete. `~` is expanded. |
Returns
boolean
`true` on success, `false` on failure.
Example
hs.fs.deletePath("/tmp/old.txt")
hs.fs.list(path) -> [String]
List the immediate contents of a directory.
Returns bare filenames (not full paths), sorted alphabetically.
The `.` and `..` entries are never included.
Declaration
hs.fs.list(path) -> [String]
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to the directory. `~` is expanded. |
Returns
[String]
Sorted array of filenames, or `null` if the path cannot be read.
Example
const files = hs.fs.list("~/Documents")
hs.fs.listRecursive(path) -> [String]
Recursively list all entries under a directory.
Returns paths relative to `path`, sorted alphabetically.
Declaration
hs.fs.listRecursive(path) -> [String]
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to the root directory. `~` is expanded. |
Returns
[String]
Sorted array of relative paths, or `null` if the path cannot be read.
Example
const all = hs.fs.listRecursive("~/Documents")
hs.fs.mkdir(path) -> boolean
Create a directory, including all necessary intermediate directories.
Succeeds silently if the directory already exists.
Declaration
hs.fs.mkdir(path) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path of the directory to create. `~` is expanded. |
Returns
boolean
`true` on success, `false` on failure.
Example
hs.fs.mkdir("~/Projects/new-thing")
hs.fs.rmdir(path) -> boolean
Remove an empty directory.
Fails if the directory is not empty. Use `deletePath` to remove a non-empty
directory recursively.
Declaration
hs.fs.rmdir(path) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path of the directory to remove. `~` is expanded. |
Returns
boolean
`true` on success, `false` on failure.
Example
hs.fs.rmdir("/tmp/empty-dir")
hs.fs.currentDir() -> string
Returns the current working directory of the process.
Declaration
hs.fs.currentDir() -> string
Returns
string
Current directory path, or `null` on error.
Example
console.log(hs.fs.currentDir())
hs.fs.chdir(path) -> boolean
Change the current working directory of the process.
Declaration
hs.fs.chdir(path) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | New working directory path. `~` is expanded. |
Returns
boolean
`true` on success, `false` on failure.
Example
hs.fs.chdir("~/Projects")
hs.fs.pathToAbsolute(path) -> string
Resolve a path to its absolute, canonical form.
Expands `~`, resolves `.` and `..`, and follows all symbolic links.
Returns `null` if any component of the path does not exist.
Declaration
hs.fs.pathToAbsolute(path) -> string
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to resolve. |
Returns
string
Absolute canonical path, or `null` if it cannot be resolved.
Example
console.log(hs.fs.pathToAbsolute("~/Library"))
hs.fs.displayName(path) -> string
Return the localised display name for a file or directory as shown by Finder.
For example, `/Library` appears as `"Library"` in Finder even though its
on-disk name is the same.
Declaration
hs.fs.displayName(path) -> string
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to the file or directory. `~` is expanded. |
Returns
string
Display name string, or `null` if the path does not exist.
Example
console.log(hs.fs.displayName("/Library"))
hs.fs.temporaryDirectory() -> string
Returns the temporary directory for the current user.
Declaration
hs.fs.temporaryDirectory() -> string
Returns
string
Temporary directory path (always ends with `/`).
Example
console.log(hs.fs.temporaryDirectory())
hs.fs.homeDirectory() -> string
Returns the home directory for the current user.
Declaration
hs.fs.homeDirectory() -> string
Returns
string
Home directory path string.
Example
console.log(hs.fs.homeDirectory())
hs.fs.urlFromPath(path) -> string
Returns a `file://` URL string for the given path.
Declaration
hs.fs.urlFromPath(path) -> string
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Filesystem path. `~` is expanded. |
Returns
string
URL string
Example
console.log(hs.fs.urlFromPath("/tmp/foo.txt"))
// → "file:///tmp/foo.txt"
hs.fs.attributes(path) -> NSDictionary
Get metadata attributes for a file or directory.
Does not follow symbolic links. Use `isSymlink` to detect links before calling this if needed.
Declaration
hs.fs.attributes(path) -> NSDictionary
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to inspect. `~` is expanded. |
Returns
NSDictionary
Attributes object, or `null` if the path cannot be accessed.
Example
const info = hs.fs.attributes("/etc/hosts")
console.log(info.size, info.type)
hs.fs.touch(path) -> boolean
Update the modification timestamp of a file to the current time.
Creates the file if it does not exist (equivalent to the POSIX `touch` command).
Declaration
hs.fs.touch(path) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to the file. `~` is expanded. |
Returns
boolean
`true` on success, `false` on failure.
Example
hs.fs.touch("/tmp/marker.txt")
hs.fs.link(source, destination) -> boolean
Create a hard link at `destination` pointing at `source`.
Both paths must be on the same filesystem volume.
Declaration
hs.fs.link(source, destination) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| source | string | Path of the existing file. |
| destination | string | Path for the new hard link. |
Returns
boolean
`true` on success, `false` on failure.
Example
hs.fs.link("/tmp/a.txt", "/tmp/b.txt")
hs.fs.symlink(source, destination) -> boolean
Create a symbolic link at `destination` pointing at `source`.
Unlike hard links, symlinks may cross filesystem boundaries and may
point to paths that do not yet exist.
Declaration
hs.fs.symlink(source, destination) -> boolean
Parameters
| Name | Type | Description |
|---|---|---|
| source | string | The path the symlink will point to. |
| destination | string | The path where the symlink will be created. |
Returns
boolean
`true` on success, `false` on failure.
Example
hs.fs.symlink("/usr/local/bin", "/tmp/bin-link")
hs.fs.readlink(path) -> string
Read the target of a symbolic link without resolving it.
Declaration
hs.fs.readlink(path) -> string
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to the symbolic link. |
Returns
string
The raw path the link points to, or `null` if the path is not a symlink.
Example
console.log(hs.fs.readlink("/var"))
hs.fs.tags(path) -> [String]
Get the Finder tags assigned to a file or directory.
Declaration
hs.fs.tags(path) -> [String]
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to the file or directory. `~` is expanded. |
Returns
[String]
Array of tag name strings, or `null` if no tags are set.
Example
console.log(hs.fs.tags("~/Documents/report.pdf"))
hs.fs.fileUTI(path) -> string
Replace all Finder tags on a file or directory.
This function is only available on macOS Tahoe (26) or later.
Declaration
hs.fs.fileUTI(path) -> string
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to the file. |
Returns
string
`true` on success, `false` on failure.
Example
hs.fs.setTags("~/Documents/report.pdf", ["Important", "Work"])
hs.fs.addTags("~/Documents/report.pdf", ["Reviewed"])
hs.fs.removeTags("~/Documents/report.pdf", ["Draft"])
console.log(hs.fs.fileUTI("/etc/hosts")) // → "public.plain-text"
console.log(hs.fs.fileUTI("/tmp/foo.png")) // → "public.png"
hs.fs.pathToBookmark(path) -> string
Encode a file path as a persistent bookmark that survives file moves and renames.
The returned string is base64-encoded bookmark data that can be stored and
later resolved with `pathFromBookmark`.
Declaration
hs.fs.pathToBookmark(path) -> string
Parameters
| Name | Type | Description |
|---|---|---|
| path | string | Path to the file or directory. `~` is expanded. |
Returns
string
Base64-encoded bookmark string, or `null` on failure.
Example
const data = hs.fs.pathToBookmark("/tmp/foo.txt")
hs.fs.pathFromBookmark(data) -> string
Resolve a base64-encoded bookmark back to a file path.
Declaration
hs.fs.pathFromBookmark(data) -> string
Parameters
| Name | Type | Description |
|---|---|---|
| data | string | Base64-encoded bookmark string produced by `pathToBookmark`. |
Returns
string
The current file path, or `null` if the bookmark cannot be resolved.
Example
const path = hs.fs.pathFromBookmark(savedData)