fs
The fs module provides cross-platform filesystem operations. It is not loaded by default and must be explicitly loaded.
Loading the Module
C++ API
#include <behl/behl.hpp>
behl::State* S = behl::new_state();
behl::load_stdlib(S); // Load standard library
behl::load_lib_fs(S); // Load filesystem module
Behl Script
// Import the fs module
const fs = import("fs");
// After load_lib_fs(S, true) is called in C++
let content = fs.read("file.txt");
// Or with load_lib_fs(S, false):
let fs = import("fs");
let content = fs.read("file.txt");
File Operations
fs.open(path, mode)
Open a file and return a file handle for reading/writing.
Parameters:
path- File pathmode- Open mode:"r"- Read only"w"- Write (truncate existing)"a"- Append"r+"- Read and write"w+"- Read and write (truncate existing)"a+"- Read and append
Returns: File handle on success, false, error on failure
let file, err = fs.open("data.txt", "r");
if (!file) {
print("Open failed: " + err);
return;
}
let content = file:read();
file:close();
File Handle Methods:
file:read(size)
Read from file.
Parameters:
size- Number of bytes to read (required)
Returns: content, bytes_read - String content and actual number of bytes read
let file = fs.open("data.txt", "r");
let chunk, bytes = file:read(100); // Read up to 100 bytes
print("Read " + tostring(bytes) + " bytes");
file:close();
file:write(data)
Write string to file.
Parameters:
data- String to write
Returns: true on success, false, error on failure
let file = fs.open("output.txt", "w");
file:write("Hello, ");
file:write("World!\n");
file:close();
file:seek(whence, [offset])
Move file position.
Parameters:
whence- Position reference:"set"(beginning),"cur"(current),"end"(end)offset(optional) - Number of bytes to move (default: 0)
Returns: New position in file
let file = fs.open("data.txt", "r");
file:seek("set", 100); // Move to byte 100
let pos = file:seek("cur", 0); // Get current position
file:seek("end", 0); // Move to end
file:close();
file:close()
Close the file handle.
Returns: true
let file = fs.open("data.txt", "r");
// ... use file ...
file:close();
Note: File handles are automatically closed by the garbage collector, but it’s good practice to close them explicitly.
fs.read(path)
Read entire file contents as string (convenience function).
Returns: content on success, false, error on failure
let content, err = fs.read("config.txt");
if (!content) {
print("Error: " + err);
} else {
print("File contents: " + content);
}
fs.write(path, content)
Write string to file (overwrites existing, convenience function).
Returns: true on success, false, error on failure
let success, err = fs.write("output.txt", "Hello, World!");
if (!success) {
print("Write failed: " + err);
}
fs.append(path, content)
Append string to file (convenience function).
Returns: true on success, false, error on failure
fs.append("log.txt", "New log entry\n");
fs.exists(path)
Check if file or directory exists.
Returns: boolean
if (fs.exists("config.txt")) {
print("Config file found");
}
fs.remove(path)
Delete file or empty directory.
Returns: true if removed, false if doesn’t exist, false, error on failure
let removed, err = fs.remove("temp.txt");
if (removed) {
print("File deleted");
}
fs.rename(oldpath, newpath)
Rename or move file.
Returns: true on success, false, error on failure
let success, err = fs.rename("old.txt", "new.txt");
if (!success) {
print("Rename failed: " + err);
}
fs.copy(source, dest)
Copy file (overwrites destination).
Returns: true on success, false, error on failure
fs.copy("template.txt", "output.txt");
Directory Operations
fs.mkdir(path)
Create directory (creates parent directories if needed).
Returns: true if created, false if already exists, false, error on failure
fs.mkdir("output/data/reports"); // Creates all intermediate dirs
fs.rmdir(path)
Remove empty directory.
Returns: true if removed, false if doesn’t exist, false, error on failure
let removed, err = fs.rmdir("temp_dir");
if (!removed && err) {
print("Error: " + err);
}
fs.list(path)
List directory contents (returns 0-indexed table of filenames).
Returns: table on success, null, error on failure
let entries, err = fs.list(".");
if (entries == null) {
print("Error: " + err);
} else {
for (let i = 0; i < #entries; i++) {
print(entries[i]);
}
}
fs.cwd()
Get current working directory.
Returns: string path, or null, error on failure
let dir = fs.cwd();
print("Current directory: " + dir);
fs.chdir(path)
Change current working directory.
Returns: true on success, false, error on failure
let success, err = fs.chdir("../parent");
if (!success) {
print("chdir failed: " + err);
}
File Information
fs.is_file(path)
Check if path is a regular file.
Returns: boolean
if (fs.is_file("data.txt")) {
print("It's a file");
}
fs.is_dir(path)
Check if path is a directory.
Returns: boolean
if (fs.is_dir("output")) {
print("It's a directory");
}
fs.size(path)
Get file size in bytes.
Returns: number on success, null, error on failure
let size, err = fs.size("data.bin");
if (size != null) {
print("File is " + tostring(size) + " bytes");
}
Path Operations
fs.join(path1, path2, …)
Join path components (handles separators correctly).
Returns: string combined path
let path = fs.join("home", "user", "documents", "file.txt");
// Windows: "home\user\documents\file.txt"
// Unix: "home/user/documents/file.txt"
fs.absolute(path)
Convert relative path to absolute.
Returns: string absolute path, or null, error on failure
let abs, err = fs.absolute("../data/file.txt");
print("Absolute: " + abs);
fs.dirname(path)
Get directory part of path.
Returns: string directory
let dir = fs.dirname("/home/user/file.txt");
// dir = "/home/user"
fs.basename(path)
Get filename with extension.
Returns: string filename
let name = fs.basename("/home/user/file.txt");
// name = "file.txt"
fs.stem(path)
Get filename without extension.
Returns: string stem
let stem = fs.stem("/home/user/file.txt");
// stem = "file"
fs.extension(path)
Get file extension (including dot).
Returns: string extension
let ext = fs.extension("/home/user/file.txt");
// ext = ".txt"
Complete Examples
Read Configuration File
function loadConfig(path) {
if (!fs.exists(path)) {
return {port = 8080, host = "localhost"}; // Defaults
}
let content, err = fs.read(path);
if (content == null) {
print("Warning: " + err);
return {port = 8080, host = "localhost"};
}
// Parse content (simplified)
return parseConfig(content);
}
let config = loadConfig("config.txt");
Process All Files in Directory
function processDirectory(dir) {
let entries, err = fs.list(dir);
if (entries == null) {
print("Error listing directory: " + err);
return;
}
for (let i = 0; i < #entries; i++) {
let path = fs.join(dir, entries[i]);
if (fs.is_file(path)) {
print("Processing: " + path);
let content, err = fs.read(path);
if (content != null) {
// Process file content
processFile(content);
}
}
}
}
processDirectory("data");
Safe File Operations with Defer
function appendLog(message) {
let log_path = fs.join(fs.cwd(), "app.log");
let success, err = fs.append(log_path, message + "\n");
if (!success) {
print("Failed to write log: " + err);
}
}
function withTempFile(callback) {
let temp_path = fs.join(fs.cwd(), "temp.txt");
fs.write(temp_path, "temporary data");
defer fs.remove(temp_path); // Cleanup on exit
callback(temp_path);
}
Create Directory Structure
function ensureDirectory(path) {
if (fs.exists(path)) {
if (!fs.is_dir(path)) {
print("Error: " + path + " exists but is not a directory");
return false;
}
return true;
}
let success, err = fs.mkdir(path);
if (!success) {
print("Failed to create directory: " + err);
return false;
}
return true;
}
ensureDirectory("output/reports/2024");
File Backup
function backupFile(path) {
if (!fs.exists(path)) {
print("File not found: " + path);
return false;
}
let dir = fs.dirname(path);
let name = fs.stem(path);
let ext = fs.extension(path);
let backup_path = fs.join(dir, name + ".backup" + ext);
let success, err = fs.copy(path, backup_path);
if (!success) {
print("Backup failed: " + err);
return false;
}
print("Backed up to: " + backup_path);
return true;
}
backupFile("important.txt");
Error Handling
All operations that can fail return null or false plus an error message as a second return value:
// Pattern 1: Check for null
let content, err = fs.read("file.txt");
if (content == null) {
print("Error: " + err);
return;
}
// Pattern 2: Check boolean
let success, err = fs.write("output.txt", data);
if (!success) {
print("Error: " + err);
return;
}
// Pattern 3: Use pcall for critical operations
let ok, result, err = pcall(function() {
return fs.read("critical.txt");
});
if (!ok) {
print("Fatal error: " + result);
} elseif (result == null) {
print("Read error: " + err);
}
Platform Notes
- Path Separators: Automatically handled (
/on Unix,\on Windows) - Permissions: Some operations may require elevated privileges
- Symbolic Links: Followed by default in most operations
- Thread Safety: Not thread-safe - serialize filesystem access
- Encodings: Strings are UTF-8, but underlying filesystem encoding varies
See Also
- os Module - Operating system interface
- Defer Statement - Cleanup with defer
- Error Handling - pcall and error recovery