File system operations with babashka.fs. Use when working with file I/O, directory traversal, path manipulation, file copying/moving, temp files, archives (zip/gzip), or file metadata.
Scanned 2/12/2026
Install via CLI
openskills install majiayu000/claude-skill-registry---
name: clojure-babashka-fs
description: File system operations with babashka.fs. Use when working with file I/O, directory traversal, path manipulation, file copying/moving, temp files, archives (zip/gzip), or file metadata.
---
# babashka.fs
Cross-platform file system utilities for Clojure. Built on java.nio, works in JVM Clojure and babashka (built-in since v0.2.9).
## Setup
deps.edn:
```clojure
babashka/fs {:mvn/version "0.5.27"}
```
Leiningen:
```clojure
[babashka/fs "0.5.27"]
```
See https://clojars.org/babashka/fs for the latest version.
Note: Built into babashka, no dependency needed if using bb.
## Quick Start
```clojure
(require '[babashka.fs :as fs])
;; Check file types
(fs/directory? ".") ;; => true
(fs/exists? "project.clj") ;; => true
(fs/regular-file? "README") ;; => true
;; Path manipulation
(str (fs/path "src" "app" "core.clj"))
;; => "src/app/core.clj"
(str (fs/absolutize "."))
;; => "/home/user/project"
;; List directory
(map str (fs/list-dir "src"))
;; => ("src/app" "src/test")
;; Find files
(map str (fs/glob "." "**/*.clj"))
;; => ("src/app/core.clj" "test/app/core_test.clj")
```
## Common Operations
### Path Creation and Manipulation
```clojure
;; Build paths
(fs/path "home" "user" "file.txt") ;; => #object[java.nio.file.Path]
(fs/file "home" "user" "file.txt") ;; => #object[java.io.File]
;; Expand home directory
(fs/expand-home "~/projects")
;; => #object[java.nio.file.Path "/home/user/projects"]
;; Get components
(fs/file-name "/path/to/file.txt") ;; => "file.txt"
(fs/parent "/path/to/file.txt") ;; => #object[Path "/path/to"]
(fs/extension "file.clj") ;; => "clj"
(split-ext "file.tar.gz") ;; => ["file.tar" "gz"]
(strip-ext "file.clj") ;; => "file"
;; Normalize and resolve
(fs/normalize "path/./to/../file") ;; => "path/file"
(fs/absolutize "src") ;; => "/home/user/project/src"
(fs/canonicalize "src") ;; => resolves symlinks + normalizes
(fs/real-path "src") ;; => like canonicalize, requires existence
```
### Directory Listing and Searching
```clojure
;; List immediate children
(fs/list-dir "src")
;; => [#object[Path "src/core.clj"] #object[Path "src/util.clj"]]
;; List with glob filter
(fs/list-dir "src" "*.clj")
;; => [#object[Path "src/core.clj"]]
;; Glob search (recursive)
(fs/glob "." "**/*.clj") ;; all .clj files
(fs/glob "." "**{.clj,.cljc}") ;; .clj OR .cljc files
;; Match search (more control)
(fs/match "." "regex:.*\\.clj" {:recursive true})
;; Find files in PATH
(fs/which "java")
;; => #object[Path "/usr/bin/java"]
(fs/exec-paths) ;; all PATH directories as Paths
```
### File Operations
```clojure
;; Copy
(fs/copy "src.txt" "dest.txt")
(fs/copy "src.txt" "dest.txt" {:replace-existing true})
(fs/copy-tree "src-dir" "dest-dir") ;; recursive copy
;; Move
(fs/move "old.txt" "new.txt")
(fs/move "old.txt" "new.txt" {:replace-existing true})
;; Delete
(fs/delete "file.txt") ;; throws if not exists
(fs/delete-if-exists "file.txt") ;; safe delete
(fs/delete-tree "dir") ;; recursive delete (rm -rf)
(fs/delete-tree "dir" {:force true}) ;; delete read-only files too
```
### Creating Files and Directories
```clojure
;; Directories
(fs/create-dir "newdir") ;; single dir, parent must exist
(fs/create-dirs "path/to/newdir") ;; like mkdir -p
;; Files
(fs/create-file "empty.txt")
;; Temporary
(fs/create-temp-dir)
(fs/create-temp-dir {:prefix "myapp-"})
(fs/create-temp-file)
(fs/create-temp-file {:prefix "log-" :suffix ".txt"})
;; With auto-cleanup
(fs/with-temp-dir [tmp]
(fs/create-file (fs/path tmp "work.txt"))
;; ... do work ...
) ;; tmp deleted here
```
### Reading and Writing
```clojure
;; Read
(fs/read-all-bytes "file.bin") ;; => byte[]
(fs/read-all-lines "file.txt") ;; => ["line1" "line2"]
;; Write
(fs/write-bytes "out.bin" byte-array)
(fs/write-lines "out.txt" ["line1" "line2"])
(fs/write-lines "out.txt" ["more"] {:append true})
;; Update in place
(fs/update-file "config.edn"
#(str/replace % "localhost" "prod.example.com"))
```
### File Metadata
```clojure
;; Timestamps
(fs/creation-time "file.txt") ;; => FileTime
(fs/last-modified-time "file.txt") ;; => FileTime
(fs/file-time->millis (fs/last-modified-time "file.txt"))
;; Set timestamps
(fs/set-last-modified-time "file.txt" (System/currentTimeMillis))
;; Size
(fs/size "file.txt") ;; => bytes
;; Permissions (Unix/Linux)
(fs/posix-file-permissions "file.sh")
(fs/posix->str (fs/posix-file-permissions "file.sh"))
;; => "rwxr-xr-x"
(fs/set-posix-file-permissions "file.sh" "rwx------")
;; Owner
(str (fs/owner "file.txt")) ;; => "username"
```
### Archives
```clojure
;; Zip
(fs/zip "archive.zip" ["file1.txt" "dir/file2.txt"])
(fs/zip "archive.zip" ["src"] {:root "src"}) ;; elide "src/" in archive
;; Unzip
(fs/unzip "archive.zip") ;; extract to current dir
(fs/unzip "archive.zip" "dest-dir")
(fs/unzip "archive.zip" "dest-dir" {:replace-existing true})
;; Gzip
(fs/gzip "file.txt") ;; creates file.txt.gz
(fs/gzip "file.txt" {:out-file "archive.gz"})
;; Gunzip
(fs/gunzip "file.txt.gz") ;; extracts to current dir
(fs/gunzip "file.txt.gz" "dest-dir")
```
### Symbolic Links
```clojure
;; Check
(fs/sym-link? "link") ;; => true/false
;; Create
(fs/create-sym-link "link" "target")
(fs/create-link "hardlink" "existing") ;; hard link
;; Read target
(fs/read-link "link") ;; => Path to target
```
## Advanced Patterns
### Walking File Trees
```clojure
;; Low-level tree walker
(fs/walk-file-tree "src"
{:pre-visit-dir (fn [dir attrs]
(println "Entering" dir)
:continue)
:visit-file (fn [file attrs]
(println "File:" file)
:continue)})
;; Find modified files
(fs/modified-since "build/last-build-marker"
(fs/glob "src" "**/*.clj"))
;; => seq of files newer than marker
```
### XDG Directories
```clojure
;; Standard user directories
(fs/xdg-config-home) ;; => ~/.config
(fs/xdg-config-home "myapp") ;; => ~/.config/myapp
(fs/xdg-data-home) ;; => ~/.local/share
(fs/xdg-cache-home) ;; => ~/.cache
(fs/xdg-state-home) ;; => ~/.local/state
;; Respects XDG_CONFIG_HOME etc. env vars
```
### Cross-Platform Paths
```clojure
;; Use forward slashes on all platforms
(fs/unixify "C:\\Users\\name\\file.txt")
;; => "C:/Users/name/file.txt"
;; Separators
fs/file-separator ;; "/" or "\\" depending on OS
fs/path-separator ;; ":" or ";" for PATH-like strings
;; Platform detection
(fs/windows?) ;; => true on Windows
```
## Key Gotchas
1. **Empty string means cwd**: `(fs/list-dir "")` is same as `(fs/list-dir ".")`. Most functions treat `""` as current directory.
2. **Symlink following**: Many functions accept `:nofollow-links` or `:follow-links` options. Default behavior varies:
- Most operations follow symlinks by default
- Tree walking does NOT follow symlinks unless `:follow-links true`
- See [README](references/API.md) for function-specific defaults
3. **creation-time is unreliable**: Behavior varies by OS and Java version. On Linux it often returns modified time. See [API notes](references/API.md#creation-time).
4. **glob patterns use java.nio syntax**:
- `*` matches within directory (not across `/`)
- `**` matches across directories
- `{.clj,.cljc}` for alternation
- See [FileSystem#getPathMatcher](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/nio/file/FileSystem.html#getPathMatcher(java.lang.String))
5. **Paths vs Files**: Functions return `java.nio.file.Path` objects. Use `str` to convert to string, or `fs/file` to convert to `java.io.File`.
6. **Maps are closed by default in newer glob**: Recent versions of fs set `{:hidden false}` by default unless pattern starts with `.`
## References
- [Complete API Reference](references/API.md) - All functions with detailed docs
- GitHub: https://github.com/babashka/fs
- Clojars: https://clojars.org/babashka/fs
No comments yet. Be the first to comment!