Scripting I/O
Tova provides comprehensive I/O functions for scripting, file system operations, shell commands, and environment access. These functions are available in tova run scripts and server blocks.
For reading and writing data files (CSV, JSON, etc.), see the I/O guide.
Filesystem
The fs namespace provides file and directory operations.
fs.exists
fs.exists(path) -> BoolReturns true if the file or directory exists.
if fs.exists("config.json") {
config = read("config.json")
}fs.is_file
fs.is_file(path) -> BoolReturns true if the path is a regular file.
fs.is_dir
fs.is_dir(path) -> BoolReturns true if the path is a directory.
fs.ls
fs.ls(dir?) -> [String]Lists entries in a directory. Defaults to the current directory.
files = fs.ls("src/")
// ["main.tova", "utils.tova", "lib/"]fs.mkdir
fs.mkdir(path) -> NilCreates a directory (and parent directories if needed).
fs.mkdir("output/reports")fs.rm
fs.rm(path) -> NilRemoves a file or directory.
fs.cp
fs.cp(src, dest) -> NilCopies a file or directory.
fs.cp("template.tova", "new-project/main.tova")fs.mv
fs.mv(src, dest) -> NilMoves or renames a file or directory.
fs.read_text
fs.read_text(path) -> StringReads the entire contents of a file as a string.
content = fs.read_text("README.md")
print(len(content))fs.write_text
fs.write_text(path, content) -> NilWrites a string to a file, creating it if it does not exist.
fs.write_text("output.txt", "Hello, World!")fs.read_bytes
fs.read_bytes(path) -> BufferReads the entire contents of a file as binary data.
data = fs.read_bytes("image.png")
print("Read {len(data)} bytes")fs.glob_files
fs.glob_files(pattern) -> [String]Returns file paths matching a glob pattern.
tova_files = fs.glob_files("src/**/*.tova")
test_files = fs.glob_files("tests/*.tova")fs.file_stat
fs.file_stat(path) -> ObjectReturns file metadata (size, modified time, etc.).
fs.file_size
fs.file_size(path) -> IntReturns the file size in bytes.
size = fs.file_size("data.csv")
print("File is {size} bytes")Path Utilities
path_join
path_join(...parts) -> StringJoins path segments with the platform separator.
path_join("src", "utils", "helpers.tova")
// "src/utils/helpers.tova"path_dirname
path_dirname(path) -> StringReturns the directory portion of a path.
path_dirname("/home/user/file.tova") // "/home/user"path_basename
path_basename(path) -> StringReturns the file name portion of a path.
path_basename("/home/user/file.tova") // "file.tova"path_resolve
path_resolve(...parts) -> StringResolves a path to an absolute path.
path_ext
path_ext(path) -> StringReturns the file extension.
path_ext("data.csv") // ".csv"
path_ext("archive.tar.gz") // ".gz"path_relative
path_relative(from, to) -> StringReturns the relative path from from to to.
Symlinks
symlink
symlink(target, link_path) -> NilCreates a symbolic link.
readlink
readlink(path) -> StringReturns the target of a symbolic link.
is_symlink
is_symlink(path) -> BoolReturns true if the path is a symbolic link.
Shell Commands
sh
sh(cmd) -> StringRuns a command through the system shell and returns the stdout output.
output = sh("ls -la")
version = sh("git --version")WARNING
sh passes the command through a shell. Do not include untrusted user input in the command string. Use exec instead for safe command execution with separate arguments.
exec
exec(cmd, args) -> StringRuns a command with an explicit argument list. Arguments are passed directly to the process without shell interpretation, preventing injection vulnerabilities.
output = exec("git", ["log", "--oneline", "-5"])
result = exec("node", ["--version"])spawn
spawn(cmd, args) -> ProcessSpawns an async child process. Returns a Process object for streaming I/O.
proc = spawn("python3", ["server.py"])
await proc.wait()Environment and CLI
Building a CLI tool?
For structured CLI tools with subcommands, typed arguments, and auto-generated help, use the cli {} block instead of manual argument parsing. For terminal colors, tables, progress bars, and interactive prompts, see Terminal & CLI.
env
env(key?) -> String | ObjectReturns an environment variable value, or all environment variables if no key is given.
home = env("HOME")
all_vars = env()set_env
set_env(key, value) -> NilSets an environment variable for the current process.
set_env("NODE_ENV", "production")args
args() -> [String]Returns command-line arguments passed to the script.
arguments = args()
if len(arguments) < 2 {
print("Usage: tova run script.tova <input>")
exit(1)
}parse_args
parse_args() -> ObjectParses command-line arguments into a structured object with flags, options, and positional arguments.
opts = parse_args()
// tova run build.tova --output dist --verbose
// { output: "dist", verbose: true, _: ["build.tova"] }exit
exit(code?) -> NeverExits the process with an optional exit code (default: 0).
if error_occurred {
print("Fatal error")
exit(1)
}cwd
cwd() -> StringReturns the current working directory.
print("Working in: {cwd()}")chdir
chdir(dir) -> NilChanges the current working directory.
chdir("/tmp")
print(cwd()) // "/tmp"script_path
script_path() -> StringReturns the absolute path of the currently running script.
script_dir
script_dir() -> StringReturns the directory containing the currently running script.
on_signal
on_signal(signal, handler) -> NilRegisters a handler function for a process signal (e.g., "SIGINT", "SIGTERM").
on_signal("SIGINT", fn() {
print("Caught interrupt, cleaning up...")
cleanup()
exit(0)
})Standard Input
read_stdin
read_stdin() -> StringReads all input from stdin.
// Pipe data: echo "hello" | tova run script.tova
input = read_stdin()
print("Got: {input}")read_lines
read_lines() -> [String]Reads stdin and splits into lines.
for line in read_lines() {
process(line)
}Examples
File Processing Script
#!/usr/bin/env tova
arguments = args()
guard len(arguments) >= 1 else {
print("Usage: process.tova <input-dir>")
exit(1)
}
input_dir = arguments[0]
files = fs.glob_files(path_join(input_dir, "*.csv"))
for file in files {
data = read(file)
result = data |> where(.valid) |> sorted(fn(r) r.date)
output = replace(file, ".csv", "_clean.csv")
result |> write(output)
print("Processed {file} -> {output}")
}Environment Configuration
port = env("PORT") ?? "3000" |> to_int()
debug = env("DEBUG") == "true"
db_url = env("DATABASE_URL") ?? "sqlite:./dev.db"