Scripting with Babashka
Prerequisites: Files and I/O
Babashka (bb) is a native Clojure interpreter for scripting. It's a
single binary compiled with GraalVM that starts in milliseconds, so
Clojure becomes a practical replacement for Bash and Python scripts.
Many popular libraries are built in: clojure.java.shell, babashka.fs,
babashka.process, babashka.http-client, cheshire (JSON), YAML,
clojure.tools.cli, and core.async.
Installing and running
brew install borkdude/brew/babashka # macOS/Linux
scoop install babashka # Windows (after adding its bucket)
bb -e '(+ 1 2)'
bb script.clj arg1 arg2
A script
;; illustrative — cleanup.clj, run with: bb cleanup.clj ./logs 7
#!/usr/bin/env bb
(require '[babashka.fs :as fs]
'[clojure.string :as str])
(let [[dir days] *command-line-args*
cutoff (- (System/currentTimeMillis) (* (parse-long (or days "7")) 24 60 60 1000))
old (->> (fs/glob (or dir ".") "**.log")
(filter #(< (.toMillis (fs/last-modified-time %)) cutoff)))]
(doseq [f old]
(println "deleting" (str f))
(fs/delete f))
(println (count old) "file(s) removed"))
Running processes and calling APIs
;; illustrative — needs git and network access
(require '[babashka.process :refer [shell process]]
'[babashka.http-client :as http]
'[cheshire.core :as json])
(shell "git" "status" "--short") ; inherit output
(def branch (-> (process {:out :string} "git rev-parse --abbrev-ref HEAD") deref :out str/trim))
(def repo (-> (http/get "https://api.github.com/repos/babashka/babashka")
:body
(json/parse-string true)))
(println branch (:stargazers_count repo))
Tasks: a Makefile in Clojure
;; illustrative — bb.edn
{:tasks
{test {:doc "Run tests" :task (shell "clj -X:test")}
lint {:doc "Lint sources" :task (shell "clj-kondo --lint src test")}
ci {:doc "Lint, then test" :depends [lint test]}
clean {:task (babashka.fs/delete-tree "target")}}}
bb tasks # list tasks with their docs
bb ci # run lint, then test
bb.edn tasks are a popular replacement for Makefiles and npm scripts
in Clojure projects — and in non-Clojure projects too.
Limits
Babashka runs an interpreter, so CPU-heavy code is slower than JVM Clojure, and it can only use the Java classes compiled into it (plus Babashka-compatible libraries, called pods for native extensions). For scripts, CI glue, and small tools, it's ideal.