(sigil fs)
(sigil fs) - Filesystem Operations
Work with files and directories: read metadata, traverse trees, find files with glob patterns, and manage temporary files. Complements (sigil io) for reading/writing and (sigil path) for path manipulation.
(import (sigil fs))
;; Read a config file
(define config (read-file-string "settings.json"))
;; Find all Scheme files
(glob "src/**/*.sgl") ; => ("src/main.sgl" "src/lib/utils.sgl" ...)
;; Walk a directory tree
(directory-walk "project"
(lambda (path type)
(println "~a: ~a" type path)
#t)) ; Return #t to recurse into directoriesExports
realpathprocedureReturn the canonicalized absolute pathname.
Resolves all symbolic links, . and .. components, and returns an absolute path. Returns #f if the path doesn't exist.
(realpath ".") ; => "/home/user/project"
(realpath "../other") ; => "/home/user/other"
(realpath "/tmp/../tmp") ; => "/tmp"
(realpath "nonexistent") ; => #fpath-resolveprocedureResolve a path relative to a base directory.
If path is absolute, returns it unchanged. Otherwise joins it with the base directory.
(path-resolve "/home/user" "file.txt") ; => "/home/user/file.txt"
(path-resolve "/home/user" "/tmp/x") ; => "/tmp/x"
(path-resolve "." "src/main.sgl") ; => "./src/main.sgl"file-exists?procedureCheck if a file or directory exists.
(file-exists? "config.txt") ; => #t or #f
(file-exists? "nonexistent") ; => #ffile?procedureCheck if a path is a regular file.
Returns #f for directories, symlinks, and non-existent paths.
(file? "main.sgl") ; => #t
(file? "src") ; => #f (it's a directory)directory?procedureCheck if a path is a directory.
(directory? "src") ; => #t
(directory? "main.sgl") ; => #ffile-statprocedureGet detailed file metadata.
Returns a list containing file information, or #f if the file doesn't exist. Pass #f as second argument to not follow symlinks. Use the stat-* accessor functions to extract specific fields.
(define info (file-stat "myfile.txt"))
(stat-size info) ; => 1234
(stat-mtime info) ; => 1702847123 (Unix timestamp)
(stat-type info) ; => 'regular
;; Don't follow symlinks
(file-stat "link" #f)file-sizeprocedureGet file size in bytes.
Returns #f if file doesn't exist.
(file-size "data.bin") ; => 4096
(file-size "missing") ; => #ffile-mtimeprocedureGet file modification time as Unix timestamp.
Returns #f if file doesn't exist.
(file-mtime "file.txt") ; => 1702847123file-atimeprocedureGet file access time as Unix timestamp.
Returns #f if file doesn't exist.
(file-atime "file.txt") ; => 1702850000file-ctimeprocedureGet file creation/change time as Unix timestamp.
On Unix systems this is the inode change time (ctime), not creation time. Returns #f if file doesn't exist.
(file-ctime "file.txt") ; => 1702847100stat-sizeprocedureExtract file size from a stat result.
(stat-size (file-stat "file.txt")) ; => 1234stat-mtimeprocedureExtract modification time from a stat result.
(stat-mtime (file-stat "file.txt")) ; => 1702847123stat-atimeprocedureExtract access time from a stat result.
(stat-atime (file-stat "file.txt")) ; => 1702850000stat-ctimeprocedureExtract creation/change time from a stat result.
(stat-ctime (file-stat "file.txt")) ; => 1702847100stat-modeprocedureExtract file mode (permissions) from a stat result.
Returns the raw Unix file mode bits.
(stat-mode (file-stat "script.sh")) ; => 33261 (0o100755)stat-uidprocedureExtract owner user ID from a stat result.
(stat-uid (file-stat "file.txt")) ; => 1000stat-gidprocedureExtract owner group ID from a stat result.
(stat-gid (file-stat "file.txt")) ; => 1000stat-nlinkprocedureExtract hard link count from a stat result.
(stat-nlink (file-stat "file.txt")) ; => 1stat-devprocedureExtract device ID from a stat result.
(stat-dev (file-stat "file.txt")) ; => 2049stat-inoprocedureExtract inode number from a stat result.
(stat-ino (file-stat "file.txt")) ; => 1234567stat-typeprocedureExtract file type from a stat result.
Returns a symbol: regular, directory, symlink, socket, fifo, block-device, char-device, or unknown.
(stat-type (file-stat "file.txt")) ; => 'regular
(stat-type (file-stat "src")) ; => 'directory
(stat-type (file-stat "link" #f)) ; => 'symlinksymlink?procedureTest if path is a symbolic link.
Does not follow the link—checks the link itself.
(symlink? "mylink") ; => #t if symlink
(symlink? "regular") ; => #fsocket-file?procedureTest if path is a Unix socket file.
(socket-file? "/var/run/docker.sock") ; => #tfifo?procedureTest if path is a named pipe (FIFO).
(fifo? "mypipe") ; => #t if named pipeblock-device?procedureTest if path is a block device.
(block-device? "/dev/sda") ; => #tchar-device?procedureTest if path is a character device.
(char-device? "/dev/tty") ; => #tmake-symlinkprocedureCreate a symbolic link.
Creates a symlink at link-path pointing to target. The target does not need to exist.
(make-symlink "actual-file.txt" "shortcut")
(make-symlink "/usr/bin/python3" "python")read-symlinkprocedureRead the target of a symbolic link.
Returns the path the symlink points to, without resolving it. Returns #f if path is not a symlink.
(read-symlink "python") ; => "/usr/bin/python3"copy-fileprocedureCopy a file to a new location.
Copies the contents of source to destination. The destination file is created or overwritten.
(copy-file "original.txt" "backup.txt")
(copy-file "src/main.sgl" "dist/main.sgl")touchprocedureUpdate file timestamps or create an empty file.
If the file exists, updates its access and modification times to now. If it doesn't exist, creates an empty file.
(touch "newfile.txt") ; Creates empty file
(touch "existing.txt") ; Updates timestampsdelete-fileprocedureDelete a file.
Raises an error if the file doesn't exist or can't be deleted.
(delete-file "temp.txt")rename-fileprocedureRename or move a file.
(rename-file "old-name.txt" "new-name.txt")make-directoryprocedureCreate a directory.
Creates a single directory. Parent directories must exist. Use ensure-directory to create parent directories as needed.
(make-directory "output")directory-listprocedureList the contents of a directory.
Returns a list of filenames (not full paths). Does not include . or ...
(directory-list "src") ; => ("main.sgl" "utils.sgl" "lib")read-file-stringprocedureRead entire file contents as a string.
Convenient for small to medium files. For large files or streaming, use (sigil io) port functions instead.
(define config (read-file-string "config.json"))
(define source (read-file-string "src/main.sgl"))read-file-bytesprocedureRead entire file contents as a bytevector.
For binary files like images, audio, etc.
(define image (read-file-bytes "photo.png"))write-file-stringprocedureWrite a string to a file.
Creates the file if it doesn't exist, or replaces its contents if it does. Parent directories must exist.
(write-file-string "output.txt" "Hello, World!")
(write-file-string "data.json" (json-encode data))write-file-bytesprocedureWrite a bytevector to a file.
Creates the file if it doesn't exist, or replaces its contents.
(write-file-bytes "output.bin" my-bytevector)directory-listprocedureList entries in a directory.
Returns a list of file and directory names (not full paths), excluding . and ... Returns #f if directory doesn't exist.
(directory-list "src")
; => ("main.sgl" "lib" "utils.sgl")
(directory-list "nonexistent") ; => #fdelete-directoryprocedureRemove an empty directory.
The directory must be empty. Use directory-walk to remove contents first if needed.
(delete-directory "empty-dir")ensure-directoryprocedureCreate directory and all parent directories.
Like mkdir -p—creates any missing parent directories. Does nothing if the directory already exists.
(ensure-directory "build/lib/sigil")
(ensure-directory "output")directory-walkprocedureWalk a directory tree, calling a procedure on each entry.
The procedure receives two arguments: the full path and a type symbol (file, directory, or other). For directories, return #t to recurse into them, or #f to skip.
;; Print all files
(directory-walk "src"
(lambda (path type)
(when (eq? type 'file)
(println "~a" path))
#t))
;; Count files, skip hidden directories
(let ((count 0))
(directory-walk "project"
(lambda (path type)
(cond
((eq? type 'file) (set! count (+ count 1)) #t)
((eq? type 'directory)
(not (string-starts-with? (path-basename path) ".")))
(else #t))))
count)directory-walk-listprocedureCollect all paths in a directory tree.
Returns a flat list of all files and directories, recursively.
(directory-walk-list "src")
; => ("src/main.sgl" "src/lib" "src/lib/utils.sgl" ...)directory-findprocedureFind files matching a predicate.
Walks the directory tree and returns paths where the predicate returns #t. The predicate receives path and type.
;; Find all .sgl files
(directory-find "src"
(lambda (path type)
(and (eq? type 'file)
(string-ends-with? path ".sgl"))))
;; Find empty directories
(directory-find "project"
(lambda (path type)
(and (eq? type 'directory)
(null? (directory-list path)))))find-filesprocedureRecursively find all files in a directory.
Returns a list of all file paths (not directories) under the given path, optionally filtered by a predicate. Hidden files (starting with .) are skipped by default.
;; All files recursively
(find-files "src")
; => ("src/main.sgl" "src/lib/utils.sgl" ...)
;; Filter to only .md files
(find-files "docs"
(lambda (path) (string-ends-with? path ".md")))
; => ("docs/index.md" "docs/guide/intro.md" ...)globprocedureFind files matching a glob pattern. Returns a list of matching file paths.
Supports shell-style wildcards: * matches any characters except /, and ** matches any path including subdirectories.
(glob "*.sgl") ; Files in current directory
(glob "src/*.sgl") ; Files in src/
(glob "src/**/*.sgl") ; All .sgl files recursively
(glob "test/*-test.sgl") ; Test filesmake-temp-fileprocedureCreate a temporary file.
Returns the path to a new unique temporary file. The file is created empty. Remember to delete it when done.
(define tmp (make-temp-file))
(write-file-string tmp "temporary data")
;; ... use the file ...
(delete-file tmp)make-temp-directoryprocedureCreate a temporary directory.
Returns the path to a new unique temporary directory. Remember to remove it when done (it must be empty first).
(define tmp-dir (make-temp-directory))
;; ... use the directory ...
(delete-directory tmp-dir)call-with-temp-fileprocedureUse a temporary file, then automatically delete it.
Creates a temp file, calls proc with its path, deletes the file, and returns the result of proc.
(call-with-temp-file
(lambda (path)
(write-file-string path "test data")
(run-external-tool path)))call-with-temp-directoryprocedureUse a temporary directory, then automatically remove it.
Creates a temp directory, calls proc with its path, removes the directory, and returns the result of proc. The directory must be empty when proc returns.
(call-with-temp-directory
(lambda (dir)
(let ((file (path-join dir "output.txt")))
(write-file-string file "result")
(read-file-string file))))
; Note: would fail - directory not empty!current-directoryprocedureGet the current working directory.
(current-directory) ; => "/home/user/project"set-current-directory!procedureChange the current working directory.
(set-current-directory! "/tmp")
(current-directory) ; => "/tmp"with-current-directoryprocedureExecute code in a different directory, then restore.
Changes to the given directory, runs thunk, then restores the original directory regardless of how thunk exits.
(with-current-directory "/tmp"
(lambda ()
(write-file-string "output.txt" "data")
(current-directory)))
; => "/tmp"
(current-directory)
; => "/home/user/project" (restored)