fs
The fs module provides functions and types for filesystem operations.
Ordinary metadata such as size, timestamps, ownership, permissions, and file
attributes are available through Metadata and
set_metadata. Extended attributes use
xattrs and related functions. POSIX ACLs
use acl and
set_acl. Windows security descriptors
can also be fetched and manipulated with full fidelity; see
sec_desc and the
Security Guide. Windows alternate data streams are
listed with streams.
Types
| Type | Description |
|---|---|
| Path | Supertype for filesystem paths |
| UnixPath | Unix path object |
| WindowsPath | Windows path object |
| Metadata | Immutable filesystem metadata |
| DirEntry | Directory entry object |
| XattrEntry | Extended attribute entry |
| StreamEntry | Alternate data stream entry |
Resolution modes
Many functions accept a resolve: parameter that controls how symbolic links
and other recursive path resolution is handled. Two values are accepted:
:TARGET:— Resolve all links to their final target. This is the default for most functions.:LINK:— Resolve all links except the final component. For example, given a symlinklink -> target,metadata link resolve: :LINK:returns the link's own metadata rather than the target's. This is the default forglob.
On Unix, :LINK: corresponds to lstat-style behavior. On Windows, it applies
to both symbolic links and other reparse points such as directory junctions.
Functions
open path mode? func?
Opens a file and returns a File object.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str |
Path to the file to open |
mode |
Str |
File access mode (default: "r") |
func |
func | Callable to run with the file; auto-closes when done |
File modes:
| Mode | Description |
|---|---|
"r" |
Read-only |
"w" |
Write-only (truncates existing file) |
"a" |
Append to existing file |
"r+" |
Read and write |
"w+" |
Read and write (truncates existing file) |
"a+" |
Read and append |
Add "b" suffix for binary mode (e.g., "rb", "wb", "r+b").
Returns
File
Example
# Read a file (auto-closed when block finishes)
open config.txt r do |file|
let content = file.read()
echo "Content: $content"
# Write with automatic cleanup
open output.txt w do |file|
file.write "Hello, World!"
# Manual file management
let file = open data.txt w
file.write "some data"
file.close()
remove path... :all? :ignore?
Removes one or more paths from the filesystem.
By default this removes a single file or symlink. With all: true, it also
removes directories recursively, similar to rm -r. With ignore: true,
missing paths are treated as success.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
One or more paths to remove |
all |
Bool |
If true, removes directories recursively |
ignore |
Bool |
If true, ignores a missing path |
Example
write "temp.txt" "temporary data"
remove "temp.txt"
remove "missing.txt" ignore: true
remove "build" all: true
remove "a.txt" "b.txt"
exists path
Checks whether a file or directory exists at the given path.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to check for existence |
Returns
Bool - true if the path
exists, false otherwise
Example
# Check before removing
if exists "temp.txt"
remove "temp.txt"
echo "Removed temp.txt"
else
echo "temp.txt does not exist"
# Conditional file operations
if exists "config.yaml"
echo "Found config file"
read path mode?
Reads the entire contents of a file in one call.
By default, returns text as a Str. If mode is
"b", returns raw bytes as Bin.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to the file to read |
mode |
Str |
Optional mode string; only "b" is allowed |
Returns
Example
write path content
Writes the entire contents of a file in one call, creating or truncating the file.
Binary values are written as raw bytes and strings as UTF-8 text.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to the file to write |
content |
Str|Bin |
Value to write |
Returns
Int - Number of bytes written
Example
append path content
Appends content to a file, creating it if needed.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to the file to append |
content |
Str|Bin |
Content to append |
Returns
Int - Number of bytes written
Example
set_size path size
Truncates the file at the given path to the specified byte length, creating it if needed.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to the file |
size |
Int |
New file length in bytes |
Example
is_absolute path
Checks whether a path is absolute.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to check |
Returns
Bool - true if the path is absolute,
false if relative
Example
# Check different paths
if is_absolute "/etc/passwd"
echo "Absolute path"
if !is_absolute "./config.txt"
echo "Relative path"
home_dir()
Returns the current user's home directory as a Path.
Platform behavior:
| Platform | Result |
|---|---|
| Unix | env["HOME"], or home directory from passwd database if unset |
| Windows | FOLDERID_Profile, typically C:\Users\<user> |
Returns
cache_dir :app?
Returns the platform-native user cache directory as a Path.
When app is given, the result is scoped to that application.
Parameters
| Name | Type | Description |
|---|---|---|
app |
Str? |
Application name |
Platform behavior
Without app, the base directories are:
| Platform | Result |
|---|---|
| Non-macOS Unix | $XDG_CACHE_HOME, otherwise ~/.cache |
| macOS | (home_dir() / "Library" / "Caches") |
| Windows | FOLDERID_LocalAppData, typically (home_dir() / "AppData" / "Local") |
With app: myapp:
| Platform | Result |
|---|---|
| Non-macOS Unix | (cache_dir() / "myapp)" |
| macOS | (cache_dir() / "myapp)" |
| Windows | (cache_dir() / "myapp" / "Cache") |
Returns
Example
metadata path :resolve?
Gets file metadata for the given path.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to the file or directory |
resolve |
:TARGET:|:LINK: |
Resolution mode (see above) |
Returns
Fields:
| Field | Type | Description |
|---|---|---|
len |
Int |
File size in bytes |
type |
Sym |
File type: :FILE:, :DIR:, :SYMLINK:, :FIFO:, :CHAR_DEVICE:, :BLOCK_DEVICE:, :SOCKET:, or :UNKNOWN: |
Optional timestamps (platform-dependent):
| Field | Type | Description |
|---|---|---|
modified |
DateTime |
Last modification time |
accessed |
DateTime |
Last access time |
created |
DateTime |
Creation/change time |
Unix-only (these fields do not exist on Windows):
| Field | Type | Description |
|---|---|---|
mode |
Int |
File permissions and type (stat mode) |
dev |
Int |
Device ID |
ino |
Int |
Inode number |
nlink |
Int |
Number of hard links |
uid |
Int |
User ID of owner |
gid |
Int |
Group ID of owner |
rdev |
Int |
Device ID (if special file) |
blksize |
Int |
Preferred block size for I/O |
blocks |
Int |
Number of 512-byte blocks allocated |
Windows-only (these fields do not exist on Unix):
| Field | Type | Description |
|---|---|---|
win_attrs |
Int |
Raw Windows file attribute bitmask |
Example
let meta = metadata "data.txt"
echo "Size: $(meta.size)"
echo "Type: $(meta.type)"
if (sys.os_info().family != :WINDOWS:)
echo "Mode: $(meta.mode)"
else
echo "Attributes: $(meta.win_attrs)"
# Get symlink metadata without following
let link_meta = metadata "link.txt" resolve: :LINK:
echo "Link type: $(link_meta.type)"
fs_metadata path :resolve?
Gets filesystem metadata for the filesystem containing the given path.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to resolve |
resolve |
:TARGET:|:LINK: |
Resolution mode (see above) |
Returns
Errors
| Exception | Condition |
|---|---|
sys.UnsupportedError |
On Linux, resolve: :LINK: is used |
let meta = fs_metadata "data.txt"
echo "Capacity: $(meta.capacity)"
echo "Available: $(meta.available)"
echo "Readonly: $(meta.read_only)"
sec_desc path :owner? :group? :dacl? :sacl? :resolve?
Gets selected parts of a Windows security descriptor.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to query |
owner |
Bool |
Load the owner SID |
group |
Bool |
Load the primary group SID |
dacl |
Bool |
Load the discretionary ACL |
sacl |
Bool |
Load the system ACL |
resolve |
:TARGET:|:LINK: |
Resolution mode (see above) |
Returns
set_sec_desc path desc :resolve?
Applies the components selected by a Windows security descriptor's mask.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to update |
desc |
security.windows.SecDesc |
Security descriptor to apply |
resolve |
:TARGET:|:LINK: |
Resolution mode (see above) |
acl path :default? :resolve?
Gets the POSIX.1e ACL stored on a path.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to query |
default |
Bool |
Query the directory's inheritable default ACL |
resolve |
:TARGET:|:LINK: |
Resolution mode (see above) |
Returns
security.unix.Acl, or nil when no ACL metadata
is stored.
Linux and FreeBSD support this operation. Other targets raise
sys.UnsupportedError.
set_acl path acl :default? :resolve?
Sets or removes a POSIX.1e ACL.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to update |
acl |
security.unix.Acl|nil |
ACL to set, or nil to remove it |
default |
Bool |
Update the directory's inheritable default ACL |
resolve |
:TARGET:|:LINK: |
Resolution mode (see above) |
Linux and FreeBSD support this operation. Other targets raise
sys.UnsupportedError.
xattrs path :namespace? :resolve?
Lists extended attributes for the given path.
On Windows, this uses NTFS extended attributes. Returned names may differ in case from the requested name.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to query |
namespace |
Str|Sym? |
Namespace to query; :USER: and :SYSTEM: name well-known namespaces, and :ANY: lists all namespaces |
resolve |
:TARGET:|:LINK: |
Resolution mode (see above) |
Returns
iterator of XattrEntry
streams path :resolve?
Lists alternate data streams for the given path.
This is only supported on Windows.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to query |
resolve |
:TARGET:|:LINK: |
Resolution mode (see above) |
Returns
Iter of StreamEntry
let path = Path data.txt
open $path r do |file|
for stream = file.streams()
echo "$(stream.name) $(stream.type)"
echo (path / stream)
xattr path name :namespace? :resolve?
Gets an extended attribute value.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to query |
name |
Str|XattrEntry |
Attribute name or entry from xattrs |
namespace |
Str|Sym? |
Namespace to query |
resolve |
:TARGET:|:LINK: |
Resolution mode (see above) |
Returns
set_xattr path name value :namespace? :resolve?
Sets an extended attribute value.
On Windows, empty values are rejected. NTFS deletes the attribute instead of storing an empty value.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to update |
name |
Str|XattrEntry |
Attribute name or entry from xattrs |
value |
Str|Bin |
Attribute bytes; strings use UTF-8 |
namespace |
Str|Sym? |
Namespace to update |
resolve |
:TARGET:|:LINK: |
Resolution mode (see above) |
remove_xattr path name :namespace? :resolve?
Removes an extended attribute.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to update |
name |
Str|XattrEntry |
Attribute name or entry from xattrs |
namespace |
Str|Sym? |
Namespace to update |
resolve |
:TARGET:|:LINK: |
Resolution mode (see above) |
copy from to :all?
Copies a filesystem entry from one location to another.
By default this copies a single file or symlink. With all: true, it also
copies directories recursively.
Parameters
| Name | Type | Description |
|---|---|---|
from |
Str|Path |
Source path |
to |
Str|Path |
Destination path |
all |
Bool |
If true, allows recursive directory copy |
Example
rename from to :replace?
Renames (moves) a file or directory.
By default, this replaces an existing destination. Set replace to false
to fail atomically instead.
Note
replace: false is not supported on FreeBSD.
Parameters
| Name | Type | Description |
|---|---|---|
from |
Str|Path |
Source path |
to |
Str|Path |
Destination path |
replace |
Bool? |
Whether to replace an existing destination |
Example
rename "old_name.txt" "new_name.txt"
# Move to different directory
rename "file.txt" "subdir/file.txt"
# Fail if the destination exists
rename "draft.txt" "published.txt" replace: false
move from to :all?
Moves a filesystem entry from one location to another.
This first tries a plain rename. If that fails because the source and
destination are on different filesystems, it falls back to copy-and-delete.
By default this moves a single file or symlink. With all: true, it also
moves directories recursively.
Parameters
| Name | Type | Description |
|---|---|---|
from |
Str|Path |
Source path |
to |
Str|Path |
Destination path |
all |
Bool |
If true, allows recursive directory move |
Example
symlink src dst
Creates a symbolic link at dst pointing to src.
Platform Notes:
- Unix: Creates a standard symbolic link
- Windows: Attempts to determine if the target is a file or directory by
reading its metadata. If the target cannot be accessed, the operation fails.
For explicit control, use
symlink_fileorsymlink_dir.
Parameters
| Name | Type | Description |
|---|---|---|
src |
Str|Path |
Target path the symlink points to |
dst |
Str|Path |
Path where the symlink is created |
Errors
| Exception | Condition |
|---|---|
sys.NotFoundError |
The target cannot be accessed on Windows |
Example
symlink_dir src dst
Creates a directory symbolic link at dst pointing to src.
Platform Notes:
- Unix: Equivalent to
symlink - Windows: Creates a directory symlink (requires appropriate permissions on some Windows versions)
Parameters
| Name | Type | Description |
|---|---|---|
src |
Str|Path |
Target directory path |
dst |
Str|Path |
Path where the symlink is created |
Example
symlink_file src dst
Creates a file symbolic link at dst pointing to src.
Platform Notes:
- Unix: Equivalent to
symlink - Windows: Creates a file symlink (may require appropriate permissions on some Windows versions)
Parameters
| Name | Type | Description |
|---|---|---|
src |
Str|Path |
Target file path |
dst |
Str|Path |
Path where the symlink is created |
Example
hard_link src dst
Creates a hard link at dst pointing to the existing file at src.
This uses the platform-native hard-link operation. The source must already exist, and the link must be created on the same filesystem or volume if the platform requires it.
Parameters
| Name | Type | Description |
|---|---|---|
src |
Str|Path |
Existing file to link to |
dst |
Str|Path |
Path where the hard link is created |
Example
entries path
Reads the entries in a directory.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to the directory |
Returns
Iterable of DirEntry objects
Example
# Iterate over directory entries
for entry = entries /home/user/docs
echo "$(entry.name) - $(entry.type)"
# Collect into an array
let files = [...entries "."]
echo "Found $(files.len) entries"
create_dir path :all?
Creates a directory at the given path.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to the directory to create |
all |
Bool |
If true, creates parent directories too |
Example
# Create a single directory
create_dir new_dir
# Create directory and all parents
create_dir a/b/c all: true
remove_dir path... :all? :ignore?
Removes one or more directories.
By default this removes only empty directories. With all: true, it removes
directories recursively, but only through subtrees that contain directories and
no files or other non-directory entries. Use
remove to delete directories that contain
files.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
One or more directories to remove |
all |
Bool |
If true, recursively prunes only empty directory subtrees |
ignore |
Bool |
If true, ignores missing directories and file-blocked subtrees |
Example
# Remove an empty directory
remove_dir empty_dir
# Remove an empty directory tree
remove_dir dir_to_remove all: true
# Prune only the empty branches and ignore file-blocked subtrees
remove_dir cache tmp all: true ignore: true
glob pattern :max_depth? :resolve?
Returns an iterator over paths matching a glob pattern.
Parameters
| Name | Type | Description |
|---|---|---|
pattern |
Str |
Glob pattern (e.g., "*.txt", "**/*.rs") |
max_depth |
Int |
Maximum directory depth to traverse (default: unlimited) |
resolve |
:TARGET:|:LINK: |
Resolution mode (see above) (default: :LINK:) |
Returns
Iter of Path objects
Glob pattern syntax:
*- Match any sequence of characters except path separator?- Match a single character**- Match any sequence of characters including path separators (recursive)[abc]- Match any character in the set{a,b,c}- Match any of the comma-separated patterns
Example
# Find all text files
for path = glob "*.txt"
echo "Found: $path"
# Recursive search with depth limit
for path = glob "**/*.rs" max_depth: 3
echo "Source: $path"
# Follow symlinks
for path = glob "**/*" resolve: :TARGET:
echo "Entry: $path"
normalize path
Returns a normalized path with . and .. components resolved without
accessing the filesystem.
Unresolvable .. components in relative paths are preserved.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to normalize |
Returns
Path - Normalized path
Example
# Remove redundant components
let clean = normalize "./foo/../bar/./baz"
echo $clean # bar/baz
# Works with Path objects too
let path = Path "a/b/../c"
let norm = normalize $path
echo $norm # a/c
absolute path
Returns the absolute form of a path based on the current working directory.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to make absolute |
Returns
Path - Absolute path
Example
let abs = absolute "./config.txt"
echo $abs # /current/working/dir/config.txt
# Already absolute paths are unchanged
let unchanged = absolute "/etc/passwd"
echo $unchanged # /etc/passwd
relative path base?
Returns the path relative to a base directory.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to make relative |
base |
Str|Path |
Base directory (default: cwd) |
Returns
Path - Relative path, or the original path if it
cannot be made relative
Example
# Relative to current directory
let rel = relative "/home/user/docs/file.txt"
echo $rel # docs/file.txt (if cwd is /home/user)
# Relative to specific base
let rel2 = relative "/a/b/c/d" "/a/b"
echo $rel2 # c/d
# Returns original if no common prefix
let unchanged = relative "/etc/passwd" "/home/user"
echo $unchanged # /etc/passwd
set_metadata :resolve? ...paths ...
Updates timestamps, permissions, ownership, and filesystem attributes.
Unspecified metadata is left unchanged. Unix targets support mode, numeric or
named user and group values, and applicable filesystem attributes. Windows
targets accept an account name or Sid for user
and group and support applicable filesystem attributes.
Unix supports modified and accessed timestamps; Windows also supports
created.
Paths are submitted from left to right and processing stops at the first error.
Within each path, ownership, mode, attributes, and timestamps are applied in
that order.
Backends may use multiple system operations; atomicity and rollback behavior
are unspecified.
Parameters
| Name | Type | Description |
|---|---|---|
paths |
(Str|Path)* |
Paths to update in order |
mode |
Int |
Optional Unix permission mode |
user |
Int|Str|Sid |
Optional owner ID, name, or SID |
group |
Int|Str|Sid |
Optional group ID, name, or SID |
modified |
DateTime |
Optional new modification time |
accessed |
DateTime |
Optional new access time |
created |
DateTime |
Optional new creation time (Windows only) |
resolve |
:TARGET:|:LINK: |
Resolution mode (see above) |
readonly |
Bool |
Optional readonly attribute value |
hidden |
Bool |
Optional hidden attribute/flag |
system |
Bool |
Optional system attribute value |
archive |
Bool |
Optional archive attribute value |
compressed |
Bool |
Optional compressed flag |
temporary |
Bool |
Optional temporary value |
offline |
Bool |
Optional offline value |
not_content_indexed |
Bool |
Optional indexing attribute value |
immutable |
Bool |
Optional immutable flag |
append_only |
Bool |
Optional append-only flag |
no_dump |
Bool |
Optional no-dump flag |
no_atime |
Bool |
Optional Linux no-atime flag |
no_copy_on_write |
Bool |
Optional Linux no-COW flag |
dir_sync |
Bool |
Optional Linux dir-sync flag |
casefold |
Bool |
Optional Linux casefold flag |
data_journaling |
Bool |
Optional Linux journaling flag |
no_compress |
Bool |
Optional Linux no-compress flag |
project_inherit |
Bool |
Optional Linux project flag |
secure_delete |
Bool |
Optional Linux secure-delete flag |
sync |
Bool |
Optional Linux sync flag |
no_tail_merge |
Bool |
Optional Linux no-tail flag |
top_dir |
Bool |
Optional Linux top-dir flag |
undelete |
Bool |
Optional Linux undelete flag |
direct_access |
Bool |
Optional Linux direct-access flag |
extent_format |
Bool |
Optional Linux extent flag |
opaque |
Bool |
Optional macOS opaque flag |
Errors
| Exception | Condition |
|---|---|
sys.UnsupportedError |
The operation is used on an unsupported platform |
set_metadata "script.sh" mode: 0o755 user: "deploy" group: "deploy"
set_metadata "one.txt" "two.txt" mode: 0o640
set_metadata "data.txt" hidden: true
set_metadata "data.txt" no_dump: true
set_metadata "link" group: "www-data" resolve: :LINK:
set_metadata "artifact.tar" modified: $DateTime.from_unix(1700000000)
set_metadata "cache.db" accessed: $DateTime.now()
canonical path
Returns the canonical, absolute form of a path with all intermediate components normalized and symbolic links resolved.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to canonicalize |
Returns
Path - Canonical path
Example
read_link path
Reads the target of a symbolic link.
Parameters
| Name | Type | Description |
|---|---|---|
path |
Str|Path |
Path to the symlink |
Returns
Path - The path that the symlink points to
Errors
| Exception | Condition |
|---|---|
sys.NotFoundError |
The path does not exist |
sys.PermissionDeniedError |
Permission denied to read the symlink |
sys.UnsupportedError |
Reading symlinks is not supported on this platform |
sys.Error |
Other I/O errors |
Example
with_temp_dir func
Creates a temporary directory, invokes a callable with the directory path, then removes the directory recursively upon return or error.
Parameters
| Name | Type | Description |
|---|---|---|
func |
func | Called with a Path to the temp dir |
parent |
path | Parent directory for the temporary directory |
Platform Notes:
The active VFS target chooses the default parent as follows:
- Windows: Uses the directory returned by
GetTempPath2 - Unix: Uses
TMPDIRfrom the strand-local environment if set, otherwise/tmp