Files
2025-11-29 18:47:15 +08:00

6.8 KiB

Babashka.fs Quick Reference

Setup

(require '[babashka.fs :as fs])

File Checks

(fs/exists? path)           ; Does it exist?
(fs/directory? path)        ; Is it a directory?
(fs/regular-file? path)     ; Is it a regular file?
(fs/sym-link? path)         ; Is it a symbolic link?
(fs/hidden? path)           ; Is it hidden?
(fs/readable? path)         ; Can read?
(fs/writable? path)         ; Can write?
(fs/executable? path)       ; Can execute?

Creating

(fs/create-file path)                           ; Empty file
(fs/create-dir path)                            ; Single directory
(fs/create-dirs path)                           ; With parents
(fs/create-temp-file)                           ; Temp file
(fs/create-temp-file {:prefix "x-" :suffix ".txt"})
(fs/create-temp-dir)                            ; Temp directory
(fs/create-sym-link "link" "target")           ; Symbolic link

Reading/Writing

(slurp path)                                    ; Read as string
(spit path content)                             ; Write string
(fs/read-all-lines path)                       ; Read lines
(fs/write-lines path ["line1" "line2"])        ; Write lines
(fs/read-all-bytes path)                       ; Read bytes
(fs/write-bytes path byte-array)               ; Write bytes

Copying/Moving/Deleting

(fs/copy src dest)                             ; Copy file
(fs/copy src dest {:replace-existing true})    ; Overwrite
(fs/copy-tree src dest)                        ; Copy directory
(fs/move src dest)                             ; Move/rename
(fs/delete path)                               ; Delete
(fs/delete-if-exists path)                     ; Delete (no error)
(fs/delete-tree path)                          ; Recursive delete
(fs/delete-on-exit path)                       ; Delete when JVM exits

Listing

(fs/list-dir ".")                              ; List directory
(fs/list-dir "." "*.txt")                      ; With glob
(fs/list-dirs ["dir1" "dir2"] "*.clj")        ; Multiple dirs

Searching

(fs/glob "." "**/*.clj")                       ; Recursive search
(fs/glob "." "*.{clj,edn}")                    ; Multiple extensions
(fs/match "." "regex:.*\\.clj" {:recursive true})

Common Glob Patterns

"*.txt"           ; Files ending in .txt
"**/*.clj"        ; All .clj files recursively
"**{.clj,.cljc}"  ; Multiple extensions recursive
"src/**/*_test.clj"  ; Test files under src/
"data/*.{json,edn}"  ; JSON or EDN in data/

Path Operations

(fs/path "dir" "file.txt")                     ; Join paths
(fs/file-name path)                            ; Get filename
(fs/parent path)                               ; Get parent directory
(fs/extension path)                            ; Get extension ("txt")
(fs/split-ext path)                            ; ["name" "ext"]
(fs/strip-ext path)                            ; Remove extension
(fs/components path)                           ; All path parts
(fs/absolutize path)                           ; Make absolute
(fs/relativize base target)                    ; Relative path
(fs/normalize path)                            ; Normalize (remove ..)
(fs/canonicalize path)                         ; Canonical path

Metadata

(fs/size path)                                 ; Size in bytes
(fs/creation-time path)                        ; FileTime
(fs/last-modified-time path)                   ; FileTime
(fs/file-time->millis file-time)              ; Convert to ms
(fs/owner path)                                ; Owner (Unix)
(str (fs/owner path))                          ; Owner name

System Paths

(fs/home)                                      ; User home
(fs/temp-dir)                                  ; System temp
(fs/cwd)                                       ; Current directory
(fs/exec-paths)                                ; PATH directories
(fs/which "git")                               ; Find executable

XDG Directories (Linux/Unix)

(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

Archives

(fs/zip "archive.zip" ["file1" "file2"])      ; Create zip
(fs/unzip "archive.zip" "dest-dir")           ; Extract all

Walking Trees

(fs/walk-file-tree root
  {:visit-file (fn [path attrs]
                 (println path)
                 :continue)
   :max-depth 3
   :follow-links false})

Temporary Files

;; Auto-cleanup with temp directory
(fs/with-temp-dir [tmp {}]
  (let [f (fs/path tmp "work.txt")]
    (spit f "data")
    (process f)))
;; tmp deleted here

;; Manual temp file
(let [tmp (fs/create-temp-file)]
  (try
    (spit tmp data)
    (process tmp)
    (finally (fs/delete tmp))))

Common Patterns

Find files modified in last N days

(defn recent? [days path]
  (let [cutoff (- (System/currentTimeMillis)
                  (* days 24 60 60 1000))]
    (> (fs/file-time->millis (fs/last-modified-time path))
       cutoff)))

(->> (fs/glob "." "**/*.clj")
     (filter (partial recent? 7)))

Process all files in directory

(doseq [f (fs/glob "data" "*.json")]
  (when (fs/regular-file? f)
    (process-file f)))

Safe file write (atomic)

(let [target "important.edn"
      tmp (fs/create-temp-file {:dir (fs/parent target)})]
  (try
    (spit tmp data)
    (fs/move tmp target {:replace-existing true})
    (catch Exception e
      (fs/delete-if-exists tmp)
      (throw e))))

Backup file with timestamp

(defn backup [path]
  (let [backup-name (str path ".backup."
                        (System/currentTimeMillis))]
    (fs/copy path backup-name)))

Clean old logs

(defn clean-old-logs [dir days]
  (->> (fs/glob dir "*.log")
       (remove (partial recent? days))
       (run! fs/delete)))

Tips

DO:

  • Use fs/path to join paths (cross-platform)
  • Use with-temp-dir for auto-cleanup
  • Check fs/exists? before operations
  • Use glob for finding files
  • Filter early in pipelines

DON'T:

  • Manually concatenate paths with /
  • Forget to handle missing files
  • Use list-dir for large directories (use directory-stream)
  • Forget to close streams (use with-open)

Error Handling

;; Check first
(when (fs/exists? "config.edn")
  (process-config))

;; Try-catch for specific errors
(try
  (process-file path)
  (catch java.nio.file.NoSuchFileException e
    (println "File not found"))
  (catch java.nio.file.AccessDeniedException e
    (println "Access denied")))