- name
- babashka.fs
- description
- A guide to using babashka.fs.
# Babashka.fs File System Utilities Skill
## Overview
The `babashka.fs` library is a comprehensive file system utility library for Clojure, designed for cross-platform file operations. It provides a clean, functional API for working with files, directories, and paths, built on top of Java's NIO.2 API while offering a more idiomatic Clojure interface.
**When to use this skill:**
- When working with files and directories in Clojure/Babashka scripts
- When you need cross-platform file system operations
- When writing build tasks, file processing scripts, or automation tools
- When you need to search, filter, or manipulate file systems programmatically
## Setup and Requirements
### Adding to your project
```clojure
;; In deps.edn
{:deps {babashka/fs {:mvn/version "0.5.27"}}}
;; In your namespace
(ns my-script
(:require [babashka.fs :as fs]))
```
### Built-in to Babashka
The library is built into Babashka, so no additional dependencies are needed for bb scripts:
```clojure
#!/usr/bin/env bb
(require '[babashka.fs :as fs])
(fs/directory? ".") ; => true
```
## Core Concepts
### Path Objects
Most functions accept and return `java.nio.file.Path` objects, but also work with strings and other path-like objects. The library automatically coerces between types.
```clojure
;; All of these work
(fs/exists? ".")
(fs/exists? (fs/path "."))
(fs/exists? (java.io.File. "."))
```
### Cross-Platform Support
The library handles platform differences automatically, but provides utilities when you need platform-specific behavior:
```clojure
;; Works on all platforms
(fs/path "dir" "subdir" "file.txt")
;; Convert to Unix-style paths (useful for Windows)
(fs/unixify "C:\\Users\\name\\file.txt") ; => "C:/Users/name/file.txt"
```
## Path Operations
### Creating and Manipulating Paths
```clojure
;; Create paths
(fs/path "dir" "subdir" "file.txt") ; Join path components
(fs/file "dir" "subdir" "file.txt") ; Alias for fs/path
;; Path properties
(fs/absolute? "/tmp/file.txt") ; true
(fs/relative? "dir/file.txt") ; true
(fs/hidden? ".hidden-file") ; Check if hidden
;; Path transformations
(fs/absolutize "relative/path") ; Convert to absolute
(fs/canonicalize "/tmp/../file.txt") ; Resolve to canonical form
(fs/normalize "/tmp/./dir/../file.txt") ; Normalize path
;; Path components
(fs/file-name "/path/to/file.txt") ; "file.txt"
(fs/parent "/path/to/file.txt") ; "/path/to"
(fs/extension "file.txt") ; "txt"
(fs/split-ext "file.txt") ; ["file" "txt"]
(fs/strip-ext "file.txt") ; "file"
;; Path relationships
(fs/starts-with? "/foo/bar" "/foo") ; true
(fs/ends-with? "/foo/bar.txt" "bar.txt") ; true
(fs/relativize "/foo/bar" "/foo/bar/baz") ; "baz"
;; Get all components
(fs/components "/path/to/file.txt") ; Seq of path components
```
### Working with Extensions
```clojure
;; Get extension
(fs/extension "document.pdf") ; "pdf"
(fs/extension "archive.tar.gz") ; "gz"
;; Split filename and extension
(fs/split-ext "document.pdf") ; ["document" "pdf"]
;; Remove extension
(fs/strip-ext "document.pdf") ; "document"
(fs/strip-ext "archive.tar.gz") ; "archive.tar"
```
## File and Directory Checks
```clojure
;; Existence and type checks
(fs/exists? "file.txt") ; Does it exist?
(fs/directory? "path/to/dir") ; Is it a directory?
(fs/regular-file? "file.txt") ; Is it a regular file?
(fs/sym-link? "link") ; Is it a symbolic link?
(fs/hidden? ".hidden") ; Is it hidden?
;; Permission checks
(fs/readable? "file.txt") ; Can we read it?
(fs/writable? "file.txt") ; Can we write to it?
(fs/executable? "script.sh") ; Can we execute it?
;; Comparison
(fs/same-file? "file1.txt" "file2.txt") ; Are they the same file?
```
## Creating Files and Directories
```clojure
;; Create directories
(fs/create-dir "new-dir") ; Create single directory
(fs/create-dirs "path/to/new/dir") ; Create with parents
;; Create files
(fs/create-file "new-file.txt") ; Create empty file
;; Create temporary files/directories
(fs/create-temp-file) ; Creates temp file
(fs/create-temp-file {:prefix "data-" ; Custom prefix/suffix
:suffix ".json"})
(fs/create-temp-dir) ; Creates temp directory
(fs/create-temp-dir {:prefix "workdir-"})
;; Create links
(fs/create-link "link-name" "target") ; Hard link
(fs/create-sym-link "symlink" "target") ; Symbolic link
;; Temporary directory context
(fs/with-temp-dir [tmp-dir {:prefix "work-"}]
(println "Working in" (str tmp-dir))
;; Do work with tmp-dir
;; Directory automatically deleted after
)
```
## Reading and Writing Files
### Reading Files
```clojure
;; Read entire file
(slurp (fs/file "data.txt")) ; As string
;; Read lines
(with-open [rdr (io/reader (fs/file "data.txt"))]
(doall (line-seq rdr)))
;; Or use fs helpers
(fs/read-all-lines "data.txt") ; Returns seq of lines
(fs/read-all-bytes "binary-file") ; Returns byte array
```
### Writing Files
```clojure
;; Write text
(spit (fs/file "output.txt") "Hello, world!")
;; Write lines
(fs/write-lines "output.txt"
["Line 1" "Line 2" "Line 3"])
(fs/write-lines "output.txt"
["More lines"]
{:append true}) ; Append mode
;; Write bytes
(fs/write-bytes "output.bin" byte-array)
(fs/write-bytes "output.bin" byte-array
{:append true})
```
## Copying, Moving, and Deleting
```clojure
;; Copy files
(fs/copy "source.txt" "dest.txt") ; Copy file
(fs/copy "source.txt" "dest.txt"
{:replace-existing true}) ; Overwrite if exists
;; Copy entire directory trees
(fs/copy-tree "source-dir" "dest-dir") ; Recursive copy
(fs/copy-tree "source-dir" "dest-dir"
{:replace-existing true})
;; Move/rename
(fs/move "old-name.txt" "new-name.txt") ; Move or rename
(fs/move "file.txt" "other-dir/") ; Move to directory
;; Delete
(fs/delete "file.txt") ; Delete single file
(fs/delete-if-exists "maybe-file.txt") ; No error if missing
(fs/delete-tree "directory") ; Delete directory recursively
;; Delete on exit
(fs/delete-on-exit "temp-file.txt") ; Delete when JVM exits
```
## Listing and Traversing Directories
### Simple Listing
```clojure
;; List directory contents
(fs/list-dir ".") ; Seq of paths in directory
(fs/list-dir "." "*.txt") ; With glob pattern
;; List multiple directories
(fs/list-dirs ["dir1" "dir2"] "*.clj") ; Combine results
;; Get directory stream (more efficient for large dirs)
(with-open [ds (fs/directory-stream "." "*.txt")]
(doseq [path ds]
(println path)))
```
### Walking Directory Trees
```clojure
;; Walk directory tree
(fs/walk-file-tree "."
{:visit-file (fn [path attrs]
(println "File:" path)
:continue)
:pre-visit-dir (fn [path attrs]
(println "Entering:" path)
:continue)
:post-visit-dir (fn [path ex]
(println "Leaving:" path)
:continue)})
;; Common options
;; :max-depth - limit depth
;; :follow-links - follow symbolic links
;; :visit-file - called for each file
;; :pre-visit-dir - called before visiting directory
;; :post-visit-dir - called after visiting directory
;; :visit-file-failed - called when file access fails
```
## Searching and Filtering: Glob and Match
### Glob Patterns
The `glob` function is one of the most powerful features for finding files:
```clojure
;; Find all Clojure files recursively
(fs/glob "." "**/*.clj") ; ** means recursive
;; Find files in current directory only
(fs/glob "." "*.txt") ; * means any characters
;; Multiple extensions
(fs/glob "." "**{.clj,.cljc,.cljs}") ; Match multiple patterns
;; Complex patterns
(fs/glob "src" "**/test_*.clj") ; Test files anywhere
(fs/glob "." "data/*.{json,edn}") ; JSON or EDN in data dir
;; Exclude patterns (use filter)
(->> (fs/glob "." "**/*.clj")
(remove #(re-find #"/test/" (str %)))) ; Exclude test directories
;; Common glob patterns:
;; * - matches any characters (not including /)
;; ** - matches any characters including /
;; ? - matches single character
;; [abc] - matches any character in brackets
;; {a,b} - matches either a or b
```
### Match with Regular Expressions
For more complex matching, use `match`:
```clojure
;; Use regex for pattern matching
(fs/match "." "regex:.*\\.clj$" {:recursive true})
;; Or glob (explicit)
(fs/match "." "glob:**/*.clj" {:recursive true})
;; Options
(fs/match "src" "regex:test.*\\.clj"
{:recursive true
:hidden false ; Skip hidden files
:follow-links false ; Don't follow symlinks
:max-depth 5}) ; Limit depth
```
### Practical File Filtering Examples
```clojure
;; Find large files
(->> (fs/glob "." "**/*")
(filter fs/regular-file?)
(filter #(> (fs/size %) (* 10 1024 1024))) ; > 10MB
(map str))
;; Find recently modified files
(->> (fs/glob "." "**/*.clj")
(filter #(> (fs/file-time->millis (fs/last-modified-time %))
(- (System/currentTimeMillis)
(* 24 60 60 1000)))) ; Last 24 hours
(map str))
;; Find files by owner (Unix)
(->> (fs/glob "/var/log" "*")
(filter #(= "root" (str (fs/owner %))))
(map str))
;; Find executable scripts
(->> (fs/glob "." "**/*.sh")
(filter fs/executable?)
(map str))
```
## File Metadata and Attributes
```clojure
;; File size
(fs/size "file.txt") ; Size in bytes
;; Timestamps
(fs/creation-time "file.txt") ; FileTime object
(fs/last-modified-time "file.txt") ; FileTime object
(fs/set-last-modified-time "file.txt"
(fs/file-time 1234567890000))
;; Convert FileTime to millis
(fs/file-time->millis (fs/last-modified-time "file.txt"))
Voir sur GitHub