sigildocs

(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 directories

Exports

realpathprocedure

Return 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") ; => #f
path-resolveprocedure

Resolve 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?procedure

Check if a file or directory exists.

(file-exists? "config.txt")      ; => #t or #f
(file-exists? "nonexistent")     ; => #f
file?procedure

Check 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?procedure

Check if a path is a directory.

(directory? "src")       ; => #t
(directory? "main.sgl")  ; => #f
file-statprocedure

Get 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-sizeprocedure

Get file size in bytes.

Returns #f if file doesn't exist.

(file-size "data.bin")  ; => 4096
(file-size "missing")   ; => #f
file-mtimeprocedure

Get file modification time as Unix timestamp.

Returns #f if file doesn't exist.

(file-mtime "file.txt")  ; => 1702847123
file-atimeprocedure

Get file access time as Unix timestamp.

Returns #f if file doesn't exist.

(file-atime "file.txt")  ; => 1702850000
file-ctimeprocedure

Get 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")  ; => 1702847100
stat-sizeprocedure

Extract file size from a stat result.

(stat-size (file-stat "file.txt"))  ; => 1234
stat-mtimeprocedure

Extract modification time from a stat result.

(stat-mtime (file-stat "file.txt"))  ; => 1702847123
stat-atimeprocedure

Extract access time from a stat result.

(stat-atime (file-stat "file.txt"))  ; => 1702850000
stat-ctimeprocedure

Extract creation/change time from a stat result.

(stat-ctime (file-stat "file.txt"))  ; => 1702847100
stat-modeprocedure

Extract file mode (permissions) from a stat result.

Returns the raw Unix file mode bits.

(stat-mode (file-stat "script.sh"))  ; => 33261 (0o100755)
stat-uidprocedure

Extract owner user ID from a stat result.

(stat-uid (file-stat "file.txt"))  ; => 1000
stat-gidprocedure

Extract owner group ID from a stat result.

(stat-gid (file-stat "file.txt"))  ; => 1000
stat-devprocedure

Extract device ID from a stat result.

(stat-dev (file-stat "file.txt"))  ; => 2049
stat-inoprocedure

Extract inode number from a stat result.

(stat-ino (file-stat "file.txt"))  ; => 1234567
stat-typeprocedure

Extract 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))     ; => 'symlink
symlink?procedure

Test if path is a symbolic link.

Does not follow the link—checks the link itself.

(symlink? "mylink")     ; => #t if symlink
(symlink? "regular")    ; => #f
socket-file?procedure

Test if path is a Unix socket file.

(socket-file? "/var/run/docker.sock")  ; => #t
fifo?procedure

Test if path is a named pipe (FIFO).

(fifo? "mypipe")  ; => #t if named pipe
block-device?procedure

Test if path is a block device.

(block-device? "/dev/sda")  ; => #t
char-device?procedure

Test if path is a character device.

(char-device? "/dev/tty")  ; => #t
copy-fileprocedure

Copy 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")
touchprocedure

Update 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 timestamps
delete-fileprocedure

Delete a file.

Raises an error if the file doesn't exist or can't be deleted.

(delete-file "temp.txt")
rename-fileprocedure

Rename or move a file.

(rename-file "old-name.txt" "new-name.txt")

Create a directory.

Creates a single directory. Parent directories must exist. Use ensure-directory to create parent directories as needed.

(make-directory "output")

List 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 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 entire file contents as a bytevector.

For binary files like images, audio, etc.

(define image (read-file-bytes "photo.png"))

Write 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 a bytevector to a file.

Creates the file if it doesn't exist, or replaces its contents.

(write-file-bytes "output.bin" my-bytevector)

List 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")  ; => #f

Remove an empty directory.

The directory must be empty. Use directory-walk to remove contents first if needed.

(delete-directory "empty-dir")

Create 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")

Walk 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)

Collect 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" ...)

Find 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-filesprocedure

Recursively 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" ...)
globprocedure

Find 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 files

Create 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)

Create 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)

Use 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)))

Use 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!

Get the current working directory.

(current-directory)  ; => "/home/user/project"

Change the current working directory.

(set-current-directory! "/tmp")
(current-directory)  ; => "/tmp"

Execute 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)