2019-12-30 08:51:20 +00:00
< img src = "logo/babashka.svg" width = "425px" >
2019-08-09 12:51:42 +00:00
[](https://circleci.com/gh/borkdude/babashka/tree/master)
2019-12-12 11:18:24 +00:00
[](https://app.slack.com/client/T03RZGPFR/CLX41ASCS)
2020-04-17 20:05:22 +00:00
[](https://opencollective.com/babashka) [](https://clojars.org/borkdude/babashka)
2019-08-09 12:51:42 +00:00
2019-11-07 11:46:14 +00:00
A Clojure [babushka ](https://en.wikipedia.org/wiki/Headscarf ) for the grey areas of Bash.
2019-08-12 12:42:45 +00:00
2019-12-20 15:51:41 +00:00
< blockquote class = "twitter-tweet" data-lang = "en" >
2019-12-30 12:38:05 +00:00
< p lang = "en" dir = "ltr" > Life's too short to remember how to write Bash code. I feel liberated.< / p >
2019-12-20 15:51:41 +00:00
—
< a href = "https://github.com/laheadle" > @laheadle< / a > on Clojurians Slack
< / blockquote >
2020-03-29 09:59:50 +00:00
## Introduction
The main idea behind babashka is to leverage Clojure in places where you would
be using bash otherwise.
2019-08-15 09:05:59 +00:00
2019-11-07 11:25:51 +00:00
As one user described it:
2019-11-07 11:46:14 +00:00
> I’ m quite at home in Bash most of the time, but there’ s a substantial grey area of things that are too complicated to be simple in bash, but too simple to be worth writing a clj/s script for. Babashka really seems to hit the sweet spot for those cases.
2019-11-07 11:25:51 +00:00
2020-03-29 09:59:50 +00:00
### Goals
2019-11-03 20:28:16 +00:00
2020-06-29 09:41:54 +00:00
* **Fast starting** Clojure scripting alternative for JVM Clojure
* **Easy installation:** grab the self-contained binary and run. No JVM needed.
* **Familiar:** targeted at JVM Clojure users
2020-06-29 09:43:20 +00:00
* **Cross-platform:** supports linux, macOS and Windows
2020-06-29 09:41:54 +00:00
* **Interop** with commonly used classes (`System`, `File` , `java.time.*` , `java.nio.*` )
* **Multi-threading** support (`pmap`, `future` )
* **Batteries included** (tools.cli, cheshire, ...)
2019-11-03 20:28:16 +00:00
2020-03-29 09:59:50 +00:00
### Non-goals
2019-11-03 20:28:16 +00:00
2020-08-31 12:59:57 +00:00
* Performance
2020-03-02 08:56:22 +00:00
* Provide a mixed Clojure/Bash DSL (see portability).
* Replace existing shells. Babashka is a tool you can use inside existing shells like bash and it is designed to play well with them. It does not aim to replace them.
2020-08-31 12:59:57 +00:00
### Managing expectations
2019-08-17 22:31:39 +00:00
2020-08-31 12:59:57 +00:00
Babashka uses [sci ](https://github.com/borkdude/sci ) for interpreting
Clojure. Sci implements a suffiently large subset of Clojure. Interpreting code
is in general not as performant as executing compiled code. If your script takes
more than a few seconds to run or has lots of loops, Clojure on the JVM may be a
better fit, since the performance of Clojure on the JVM outweighs its startup
time penalty. Read more about the differences with Clojure
[here ](#differences-with-clojure ).
2020-03-29 09:59:50 +00:00
### Talk
2020-04-06 19:51:53 +00:00
To get an overview of babashka, you can watch this talk ([slides](https://speakerdeck.com/borkdude/babashka-and-the-small-clojure-interpreter-at-clojured-2020)):
2020-03-29 09:55:50 +00:00
2020-04-06 19:51:53 +00:00
[](https://www.youtube.com/watch?v=Nw8aN-nrdEk)
2020-03-29 09:55:50 +00:00
2020-03-02 09:00:28 +00:00
## Quickstart
``` shellsession
2020-04-10 07:06:00 +00:00
$ curl -s https://raw.githubusercontent.com/borkdude/babashka/master/install -o install-babashka
2020-07-28 08:42:21 +00:00
$ chmod +x install-babashka & & sudo ./install-babashka
2020-04-19 18:05:52 +00:00
$ ls | bb -i '(filter #(-> % io/file .isDirectory) *input* )'
2020-03-02 09:00:28 +00:00
("doc" "resources" "sci" "script" "src" "target" "test")
bb took 4ms.
```
2020-03-02 13:19:51 +00:00
### Examples
2019-08-17 22:31:39 +00:00
2020-03-02 13:19:21 +00:00
Read the output from a shell command as a lazy seq of strings:
``` shell
$ ls | bb -i '(take 2 *input* )'
("CHANGES.md" "Dockerfile")
```
2019-08-17 22:31:39 +00:00
2020-03-02 13:19:21 +00:00
Read EDN from stdin and write the result to stdout:
2019-08-17 22:31:39 +00:00
2020-03-02 13:19:21 +00:00
``` shell
2019-12-20 22:51:24 +00:00
$ bb '(vec (dedupe *input* ))' <<< '[1 1 1 1 2]'
2019-09-08 21:31:24 +00:00
[1 2]
2020-03-02 13:19:21 +00:00
```
2019-08-15 09:16:15 +00:00
2020-03-02 13:19:21 +00:00
Read more about input and output flags
[here ](https://github.com/borkdude/babashka/#input-and-output-flags ).
2019-08-18 05:30:29 +00:00
2020-03-02 13:19:21 +00:00
Execute a script. E.g. print the current time in California using the
`java.time` API:
2019-08-09 12:51:42 +00:00
2020-03-02 13:19:21 +00:00
File `pst.clj` :
``` clojure
#!/usr/bin/env bb
2019-09-08 21:31:24 +00:00
2020-03-02 13:19:21 +00:00
(def now (java.time.ZonedDateTime/now))
(def LA-timezone (java.time.ZoneId/of "America/Los_Angeles"))
(def LA-time (.withZoneSameInstant now LA-timezone))
(def pattern (java.time.format.DateTimeFormatter/ofPattern "HH:mm"))
(println (.format LA-time pattern))
```
2019-09-08 21:31:24 +00:00
2020-03-02 13:19:21 +00:00
``` shell
$ pst.clj
05:17
2019-09-08 21:31:24 +00:00
```
2020-04-22 10:01:22 +00:00
More examples can be found [here ](doc/examples.md ).
2019-08-09 12:51:42 +00:00
2020-03-02 13:19:21 +00:00
## Status
Functionality regarding `clojure.core` and `java.lang` can be considered stable
and is unlikely to change. Changes may happen in other parts of babashka,
although we will try our best to prevent them. Always check the release notes or
2020-08-19 14:37:38 +00:00
[CHANGELOG.md ](CHANGELOG.md ) before upgrading.
2020-03-02 13:19:21 +00:00
2019-08-09 14:02:08 +00:00
## Installation
2019-08-11 07:27:45 +00:00
### Brew
2019-08-09 14:02:08 +00:00
Linux and macOS binaries are provided via brew.
Install:
brew install borkdude/brew/babashka
Upgrade:
brew upgrade babashka
2019-11-21 10:14:38 +00:00
### Arch (Linux)
`babashka` is [available ](https://aur.archlinux.org/packages/babashka-bin/ ) in the [Arch User Repository ](https://aur.archlinux.org ). It can be installed using your favorite [AUR ](https://aur.archlinux.org ) helper such as
[yay ](https://github.com/Jguer/yay ), [yaourt ](https://github.com/archlinuxfr/yaourt ), [apacman ](https://github.com/oshazard/apacman ) and [pacaur ](https://github.com/rmarquis/pacaur ). Here is an example using `yay` :
yay -S babashka-bin
2019-08-11 07:27:45 +00:00
2020-02-02 12:44:34 +00:00
### Windows
On Windows you can install using [scoop ](https://scoop.sh/ ) and the
[scoop-clojure ](https://github.com/littleli/scoop-clojure ) bucket.
2019-08-11 07:27:45 +00:00
### Installer script
Install via the installer script:
``` shellsession
2020-07-28 08:44:50 +00:00
$ curl -s https://raw.githubusercontent.com/borkdude/babashka/master/install -o install-babashka
$ chmod +x install-babashka & & sudo ./install-babashka
2019-08-11 07:27:45 +00:00
```
By default this will install into `/usr/local/bin` . To change this, provide the directory name:
``` shellsession
2020-07-28 08:44:50 +00:00
$ curl -s https://raw.githubusercontent.com/borkdude/babashka/master/install -o install-babashka
$ chmod +x install-babashka & & ./install-babashka /tmp
2019-08-11 07:27:45 +00:00
```
### Download
2020-02-23 21:56:47 +00:00
You may also download a binary from
[Github ](https://github.com/borkdude/babashka/releases ). For linux there is a
static binary available which can be used on Alpine.
2019-08-09 14:02:08 +00:00
2020-03-21 13:37:29 +00:00
## Docker
Check out the image on [Docker hub ](https://hub.docker.com/r/borkdude/babashka/ ).
2019-08-09 13:48:54 +00:00
## Usage
2019-08-09 12:51:42 +00:00
2019-08-09 17:58:11 +00:00
``` shellsession
2020-04-30 21:06:56 +00:00
Babashka v0.0.90
2019-08-18 10:33:54 +00:00
2020-04-30 21:06:56 +00:00
Options must appear in the order of groups mentioned below.
Help:
2019-08-09 17:58:11 +00:00
2019-12-28 21:52:56 +00:00
--help, -h or -? Print this help text.
--version Print the current version of babashka.
2020-04-30 21:06:56 +00:00
--describe Print an EDN map with information about this version of babashka.
In- and output flags:
2020-01-01 22:27:25 +00:00
2019-12-28 21:52:56 +00:00
-i Bind *input* to a lazy seq of lines from stdin.
-I Bind *input* to a lazy seq of EDN values from stdin.
-o Write lines to stdout.
-O Write EDN values to stdout.
--stream Stream over lines or EDN values from stdin. Combined with -i or -I *input* becomes a single value per iteration.
2020-04-30 21:06:56 +00:00
Uberscript:
2020-01-01 22:27:25 +00:00
--uberscript < file > Collect preloads, -e, -f and -m and all required namespaces from the classpath into a single executable file.
2020-04-30 21:06:56 +00:00
Evaluation:
2019-12-28 21:52:56 +00:00
-e, --eval < expr > Evaluate an expression.
-f, --file < path > Evaluate a file.
-cp, --classpath Classpath to use.
-m, --main < ns > Call the -main function from namespace with args.
2020-04-30 21:12:53 +00:00
--verbose Print entire stacktrace in case of exception.
2020-04-30 21:06:56 +00:00
REPL:
2020-02-07 08:33:02 +00:00
--repl Start REPL. Use rlwrap for history.
2019-12-28 21:52:56 +00:00
--socket-repl Start socket REPL. Specify port (e.g. 1666) or host and port separated by colon (e.g. 127.0.0.1:1666).
2020-04-03 21:51:54 +00:00
--nrepl-server Start nREPL server. Specify port (e.g. 1667) or host and port separated by colon (e.g. 127.0.0.1:1667).
2019-09-06 14:40:50 +00:00
2020-08-29 08:37:59 +00:00
If neither -e, -f, or --socket-repl are specified, then the first argument that is not parsed as a option is treated as a file if it exists, or as an expression otherwise. Everything after that is bound to *command-line-args* . Use -- to separate script command line args from bb command line args.
2019-08-18 10:33:54 +00:00
```
2019-08-14 11:38:39 +00:00
2020-09-01 21:28:16 +00:00
### Built-in namespaces
2019-08-14 11:38:39 +00:00
2020-09-01 21:27:24 +00:00
In addition to `clojure.core` , the following namespaces are available. Some are
available through pre-defined aliases in the `user` namespace, which can be
handy for one-liners. If not all vars are available, they are enumerated
2020-09-01 21:46:01 +00:00
explicitly. If some important var is missing, an issue or PR is welcome.
2020-09-01 20:46:41 +00:00
2020-09-01 21:46:01 +00:00
From Clojure:
- `clojure.core`
- `clojure.core.protocols` : `Datafiable` , `Navigable`
2020-09-01 20:46:41 +00:00
- `clojure.data`
- `clojure.datafy`
2020-09-01 21:46:01 +00:00
- `clojure.edn` aliased as `edn`
2020-09-01 20:46:41 +00:00
- `clojure.java.browse`
2019-08-18 10:33:54 +00:00
- `clojure.java.io` aliased as `io` :
2019-12-24 09:01:32 +00:00
- `as-relative-path` , `as-url` , `copy` , `delete-file` , `file` , `input-stream` ,
2019-12-29 22:37:57 +00:00
`make-parents` , `output-stream` , `reader` , `resource` , `writer`
2020-09-01 21:46:01 +00:00
- `clojure.java.shell` aliased as `shell`
- `clojure.main` : `demunge` , `repl` , `repl-requires`
- `clojure.pprint` : `pprint` (currently backed by [fipp ](https://github.com/brandonbloom/fipp )'s `fipp.edn/pprint` )
- `clojure.set` aliased as `set`
- `clojure.string` aliased as `str`
2020-01-02 12:53:35 +00:00
- `clojure.stacktrace`
2020-01-29 22:53:01 +00:00
- `clojure.test`
2020-06-28 11:24:17 +00:00
- `clojure.zip`
2020-09-01 21:46:01 +00:00
Additional libraries:
- [`babashka.curl` ](https://github.com/borkdude/babashka.curl )
- [`bencode.core` ](https://github.com/nrepl/bencode ) aliased as `bencode` : `read-bencode` , `write-bencode`
- [`cheshire.core` ](https://github.com/dakrone/cheshire ) aliased as `json`
- [`clojure.core.async` ](https://clojure.github.io/core.async/ ) aliased as
`async` . Also see [docs ](https://github.com/borkdude/babashka#coreasync ).
2019-11-11 20:14:30 +00:00
- [`clojure.data.csv` ](https://github.com/clojure/data.csv ) aliased as `csv`
2020-04-18 21:28:02 +00:00
- [`clojure.data.xml` ](https://github.com/clojure/data.xml ) aliased as `xml`
2020-09-01 21:46:01 +00:00
- [`clojure.tools.cli` ](https://github.com/clojure/tools.cli ) aliased as `tools.cli`
2020-04-15 10:22:08 +00:00
- [`clj-yaml.core` ](https://github.com/clj-commons/clj-yaml ) alias as `yaml`
2020-09-01 21:46:01 +00:00
- [`cognitect.transit` ](https://github.com/cognitect/transit-clj ) aliased as `transit`
2019-08-14 11:38:39 +00:00
2020-09-01 20:49:50 +00:00
See the
[libraries ](https://github.com/borkdude/babashka/blob/master/doc/libraries.md )
2020-09-01 21:46:01 +00:00
page for libraries that are not built-in, but which you can load from source via
the `--classpath` option.
2020-09-01 20:49:50 +00:00
2020-09-01 21:27:24 +00:00
See the [build ](https://github.com/borkdude/babashka/blob/master/doc/build.md )
2020-09-01 21:46:01 +00:00
page for built-in libraries that can be enabled via feature flags, if you want
to compile babashka yourself.
2019-12-31 13:23:54 +00:00
2020-09-01 21:27:24 +00:00
A selection of Java classes are available, see `babashka/impl/classes.clj` .
2019-11-27 13:15:29 +00:00
2020-04-24 09:12:10 +00:00
### Running a script
Scripts may be executed from a file using `-f` or `--file` :
``` shellsession
bb -f download_html.clj
```
Using `bb` with a shebang also works:
``` clojure
#!/usr/bin/env bb
(defn get-url [url]
(println "Fetching url:" url)
(let [{:keys [:exit :err :out]} (shell/sh "curl" "-sS" url)]
(if (zero? exit) out
(do (println "ERROR:" err)
(System/exit 1)))))
(defn write-html [file html]
(println "Writing file:" file)
(spit file html))
(let [[url file] *command-line-args* ]
(when (or (empty? url) (empty? file))
(println "Usage: < url > < file > ")
(System/exit 1))
(write-html file (get-url url)))
```
``` shellsession
$ ./download_html.clj
Usage: < url > < file >
$ ./download_html.clj https://www.clojure.org /tmp/clojure.org.html
Fetching url: https://www.clojure.org
Writing file: /tmp/clojure.org.html
```
If `/usr/bin/env` doesn't work for you, you can use the following workaround:
``` shellsession
$ cat script.clj
#!/bin/sh
#_(
"exec" "bb" "$0" hello "$@"
)
(prn *command-line-args* )
./script.clj 1 2 3
("hello" "1" "2" "3")
```
2019-12-20 20:17:27 +00:00
### Input and output flags
2019-08-15 04:28:00 +00:00
2019-12-20 22:51:24 +00:00
In one-liners the `*input*` value may come in handy. It contains the input read from stdin as EDN by default. If you want to read in text, use the `-i` flag, which binds `*input*` to a lazy seq of lines of text. If you want to read multiple EDN values, use the `-I` flag. The `-o` option prints the result as lines of text. The `-O` option prints the result as lines of EDN values.
2019-12-20 20:17:27 +00:00
2020-05-29 14:03:34 +00:00
> **Note:** `*input*` is only available in the `user` namespace, on other namespaces use `*in*`.
2019-12-20 20:17:27 +00:00
The following table illustrates the combination of options for commands of the form
2019-12-20 22:51:24 +00:00
echo "{{Input}}" | bb {{Input flags}} {{Output flags}} "*input*"
2019-12-20 20:17:27 +00:00
2019-12-20 22:51:24 +00:00
| Input | Input flags | Output flag | `*input*` | Output |
2019-12-20 20:17:27 +00:00
|----------------|-------------|-------------|---------------|----------|
2019-12-20 20:23:51 +00:00
| `{:a 1}` < br > `{:a 2}` | | | `{:a 1}` | `{:a 1}` |
2019-12-20 20:17:27 +00:00
| hello < br > bye | `-i` | | `("hello" "bye")` | `("hello" "bye")` |
| hello < br > bye | `-i` | `-o` | `("hello" "bye")` | hello < br > bye |
2019-12-20 20:23:51 +00:00
| `{:a 1}` < br > `{:a 2}` | `-I` | | `({:a 1} {:a 2})` | `({:a 1} {:a 2})` |
2019-12-20 20:17:27 +00:00
| `{:a 1}` < br > `{:a 2}` | `-I` | `-O` | `({:a 1} {:a 2})` | `{:a 1}` < br > `{:a 2}` |
2019-12-20 23:06:00 +00:00
When combined with the `--stream` option, the expression is executed for each value in the input:
2019-12-20 20:17:27 +00:00
``` clojure
2019-12-20 22:51:24 +00:00
$ echo '{:a 1} {:a 2}' | bb --stream '*input*'
2019-12-20 20:17:27 +00:00
{:a 1}
{:a 2}
```
2019-12-21 10:39:28 +00:00
### Current file path
2019-12-21 10:50:05 +00:00
The var `*file*` contains the full path of the file that is currently being
executed:
2019-12-21 10:39:28 +00:00
``` shellsession
$ cat example.clj
(prn *file* )
$ bb example.clj
"/Users/borkdude/example.clj"
```
2019-12-20 20:17:27 +00:00
### Command-line arguments
2020-05-26 20:47:05 +00:00
Command-line arguments can be retrieved using `*command-line-args*` . If you want
to parse command line arguments, you may use the built-in `clojure.tools.cli`
2020-09-01 21:46:01 +00:00
namespace (see
[docs ](https://github.com/borkdude/babashka#parsing-command-line-arguments )) or
use the
2020-05-26 20:47:05 +00:00
[nubank/docopt ](https://github.com/borkdude/babashka/blob/master/doc/libraries.md#nubankdocopt )
library.
2019-12-20 20:17:27 +00:00
2020-09-01 20:46:41 +00:00
### Babashka namespaces
2019-08-10 16:50:48 +00:00
2020-01-12 19:41:53 +00:00
#### babashka.classpath
2019-09-04 12:37:07 +00:00
2020-01-12 19:41:53 +00:00
Contains the function `add-classpath` which can be used to add to the classpath
dynamically:
``` clojure
2020-01-12 19:46:00 +00:00
(require '[babashka.classpath :refer [add-classpath]]
2020-08-26 08:22:45 +00:00
'[clojure.java.shell :refer [sh]]
'[clojure.string :as str])
2020-01-12 19:41:53 +00:00
(def medley-dep '{:deps {medley {:git/url "https://github.com/borkdude/medley"
:sha "91adfb5da33f8d23f75f0894da1defe567a625c0"}}})
2020-08-26 08:22:45 +00:00
(def cp (-> (sh "clojure" "-Spath" "-Sdeps" (str medley-dep)) :out str/trim))
2020-01-12 19:41:53 +00:00
(add-classpath cp)
(require '[medley.core :as m])
(m/index-by :id [{:id 1} {:id 2}]) ;;=> {1 {:id 1}, 2 {:id 2}}
```
#### babashka.wait
Contains the functions: `wait-for-port` and `wait-for-path` .
Usage of `wait-for-port` :
2019-09-04 12:37:07 +00:00
``` clojure
2019-10-14 09:24:37 +00:00
(wait/wait-for-port "localhost" 8080)
(wait/wait-for-port "localhost" 8080 {:timeout 1000 :pause 1000})
2019-09-04 12:37:07 +00:00
```
2019-10-14 09:24:37 +00:00
Waits for TCP connection to be available on host and port. Options map supports `:timeout` and `:pause` . If `:timeout` is provided and reached, `:default` 's value (if any) is returned. The `:pause` option determines the time waited between retries.
2020-01-12 19:41:53 +00:00
Usage of `wait-for-path` :
2019-10-14 09:24:37 +00:00
``` clojure
(wait/wait-for-path "/tmp/wait-path-test")
(wait/wait-for-path "/tmp/wait-path-test" {:timeout 1000 :pause 1000})
```
2020-01-12 19:41:53 +00:00
Waits for file path to be available. Options map supports `:default` , `:timeout`
and `:pause` . If `:timeout` is provided and reached, `:default` 's value (if any)
is returned. The `:pause` option determines the time waited between retries.
2020-01-12 19:50:09 +00:00
The namespace `babashka.wait` is aliased as `wait` in the `user` namespace.
2019-09-04 12:37:07 +00:00
2020-01-12 19:41:53 +00:00
#### babashka.signal
Contains the function `signal/pipe-signal-received?` . Usage:
2019-09-07 08:43:53 +00:00
``` clojure
2020-01-12 19:18:37 +00:00
(signal/pipe-signal-received?)
2019-09-07 08:43:53 +00:00
```
Returns true if `PIPE` signal was received. Example:
``` shellsession
2020-02-06 22:03:32 +00:00
$ bb '((fn [x] (println x) (when (not (signal/pipe-signal-received?)) (recur (inc x)))) 0)' | head -n2
2019-09-07 08:43:53 +00:00
1
2
```
2020-01-12 19:50:09 +00:00
The namespace `babashka.signal` is aliased as `signal` in the `user` namespace.
2020-01-12 19:41:53 +00:00
2020-03-07 13:10:58 +00:00
#### babashka.curl
The namespace `babashka.curl` is a tiny wrapper around curl. It's aliased as
`curl` in the user namespace. See
[babashka.curl ](https://github.com/borkdude/babashka.curl ).
2020-04-24 09:08:28 +00:00
## Style
A note on style. Babashka recommends the following:
2020-05-11 11:26:44 +00:00
### Explicit requires
2020-05-29 13:48:08 +00:00
Use explicit requires with namespace aliases in scripts, unless you're writing
one-liners.
2020-04-24 09:08:28 +00:00
2020-04-24 09:14:25 +00:00
Do this:
2020-04-24 09:08:28 +00:00
2020-04-24 09:14:25 +00:00
``` shell
$ ls | bb -i '(-> *input* first (str/includes? "m"))'
true
```
2020-04-24 09:08:28 +00:00
2020-04-24 09:14:25 +00:00
But not this:
2020-04-24 09:08:28 +00:00
2020-04-24 09:14:25 +00:00
script.clj:
``` clojure
(-> *input* first (str/includes? "m"))
```
2020-04-24 09:08:28 +00:00
2020-04-24 09:14:25 +00:00
Rather do this:
2020-04-24 09:08:28 +00:00
2020-04-24 09:14:25 +00:00
script.clj:
``` clojure
(ns script
(:require [clojure.java.io :as io]
[clojure.string :as str]))
2020-04-24 09:08:28 +00:00
(-> (io/reader *in* ) line-seq first (str/includes? "m"))
2020-04-24 09:14:25 +00:00
```
2020-04-24 09:08:28 +00:00
2020-04-24 09:14:25 +00:00
Some reasons for this:
2020-04-24 09:08:28 +00:00
2020-04-24 09:14:25 +00:00
- Linters like clj-kondo work better with code that uses namespace forms, explicit requires, and known Clojure constructs
- Editor tooling works better with namespace forms (sorting requires, etc).
- Writing compatible code gives you the option to run the same script with `clojure`
2020-04-24 09:08:28 +00:00
2020-04-12 14:33:20 +00:00
## [Running a REPL](doc/repl.md)
Babashka offers a REPL, a socket REPL and an nREPL server. Look
2020-04-18 07:23:04 +00:00
[here ](doc/repl.md ) for more information on how to use and integrate them with
your editor.
2020-04-12 14:33:20 +00:00
2019-12-12 22:07:35 +00:00
## Preloads
The environment variable `BABASHKA_PRELOADS` allows to define code that will be
available in all subsequent usages of babashka.
``` shellsession
BABASHKA_PRELOADS='(defn foo [x] (+ x 2))'
BABASHKA_PRELOADS=$BABASHKA_PRELOADS' (defn bar [x] (* x 2))'
export BABASHKA_PRELOADS
```
Note that you can concatenate multiple expressions. Now you can use these functions in babashka:
``` shellsession
2019-12-20 22:51:24 +00:00
$ bb '(-> (foo *input* ) bar)' << < 1
2019-12-12 22:07:35 +00:00
6
```
You can also preload an entire file using `load-file` :
``` shellsession
export BABASHKA_PRELOADS='(load-file "my_awesome_prelude.clj")'
```
2019-12-20 22:51:24 +00:00
Note that `*input*` is not available in preloads.
2019-12-12 22:07:35 +00:00
## Classpath
Babashka accepts a `--classpath` option that will be used to search for
2020-09-01 21:46:01 +00:00
namespaces when requiring them:
2019-12-12 22:07:35 +00:00
``` clojure
$ cat src/my/namespace.clj
(ns my.namespace)
(defn -main [& _args]
(println "Hello from my namespace!"))
$ bb --classpath src --main my.namespace
Hello from my namespace!
```
2020-09-01 21:46:01 +00:00
If you have a larger script with a classic Clojure project layout like
2020-04-07 15:13:05 +00:00
```shellsession
$ tree -L 3
├── deps.edn
├── README
├── src
│ └── project_namespace
│ ├── main.clj
│ └── utilities.clj
└── test
└── project_namespace
├── test_main.clj
└── test_utilities.clj
```
2020-09-01 21:46:01 +00:00
then you can tell babashka to include both the `src` and `test`
2020-04-07 15:13:05 +00:00
folders in the classpath and start a socket REPL by running:
```shellsession
$ bb --classpath src:test --socket-repl 1666
```
2019-12-12 22:07:35 +00:00
Note that you can use the `clojure` tool to produce classpaths and download dependencies:
``` shellsession
$ cat deps.edn
{:deps
2019-12-28 21:52:56 +00:00
{my_gist_script
{:git/url "https://gist.github.com/borkdude/263b150607f3ce03630e114611a4ef42"
:sha "cfc761d06dfb30bb77166b45d439fe8fe54a31b8"}}
:aliases {:my-script {:main-opts ["-m" "my-gist-script"]}}}
2019-12-12 22:07:35 +00:00
$ CLASSPATH=$(clojure -Spath)
$ bb --classpath "$CLASSPATH" --main my-gist-script
Hello from gist script!
```
If there is no `--classpath` argument, the `BABASHKA_CLASSPATH` environment
2019-12-13 17:42:38 +00:00
variable will be used:
2019-12-12 22:07:35 +00:00
``` shellsession
$ export BABASHKA_CLASSPATH=$(clojure -Spath)
$ export BABASHKA_PRELOADS="(require '[my-gist-script])"
$ bb "(my-gist-script/-main)"
Hello from gist script!
```
2020-04-13 19:16:55 +00:00
When invoking `bb` with a main function, the expression `(System/getProperty
2020-04-13 19:16:13 +00:00
"babashka.main")` will return the name of the main function.
2020-03-19 19:30:30 +00:00
Also see the
[babashka.classpath ](https://github.com/borkdude/babashka/#babashkaclasspath )
namespace which allows dynamically adding to the classpath.
2020-04-22 10:06:11 +00:00
See [deps.clj ](doc/deps.clj.md ) for a babashka script that replaces the `clojure` bash script.
2020-04-07 15:13:05 +00:00
2020-08-19 14:39:42 +00:00
## Uberscript
The `--uberscript` option collects the expressions in
`BABASHKA_PRELOADS` , the command line expression or file, the main entrypoint
and all required namespaces from the classpath into a single file. This can be
convenient for debugging and deployment.
Given the `deps.edn` from above:
``` clojure
$ deps.clj -A:my-script -Scommand "bb -cp {{classpath}} {{main-opts}} --uberscript my-script.clj"
$ cat my-script.clj
(ns my-gist-script)
(defn -main [& args]
(println "Hello from gist script!"))
(ns user (:require [my-gist-script]))
(apply my-gist-script/-main *command-line-args* )
$ bb my-script.clj
Hello from gist script!
```
Caveats:
- *Dynamic requires*. Building uberscripts works by running top-level `ns` and
`require` forms. The rest of the code is not evaluated. Code that relies on
dynamic requires may not work in an uberscript.
- *Resources*. The usage of `io/resource` assumes a classpath, so when this is
used in your uberscript, you still have to set a classpath and bring the
resources along.
If any of the above is problematic for your project, using an uberjar (see
below) is a good alternative.
## Uberjar
Babashka can create uberjars from a given classpath and optionally a main
method:
2020-08-19 15:13:05 +00:00
``` clojure
2020-08-19 15:12:14 +00:00
$ cat src/foo.clj
(ns foo (:gen-class)) (defn -main [& args] (prn :hello))
2020-08-19 15:15:56 +00:00
$ bb -cp $(clojure -Spath) -m foo --uberjar foo.jar
2020-08-19 15:12:14 +00:00
$ bb foo.jar
:hello
2020-08-19 14:39:42 +00:00
```
When producing a classpath using the `clojure` or `deps.clj` tool, Clojure
itself, spec and the core specs will be on the classpath and will therefore be
included in your uberjar, which makes it bigger than necessary:
``` shell
2020-08-19 15:12:14 +00:00
$ ls -lh foo.jar
-rw-r--r-- 1 borkdude staff 4.5M Aug 19 17:04 foo.jar
2020-08-19 14:39:42 +00:00
```
To exclude these dependencies, you can use the following `:classpath-overrides`
in your `deps.edn` :
``` clojure
2020-08-19 15:12:14 +00:00
{:aliases {:remove-clojure {:classpath-overrides {org.clojure/clojure nil
2020-08-19 14:39:42 +00:00
org.clojure/spec.alpha nil
org.clojure/core.specs.alpha nil}}}}
2020-08-19 15:12:14 +00:00
```
2020-08-19 14:39:42 +00:00
2020-08-19 15:13:05 +00:00
``` clojure
2020-08-19 15:12:14 +00:00
$ rm foo.jar
$ bb -cp $(clojure -A:remove-clojure -Spath) -m foo --uberjar foo.jar
$ bb foo.jar
:hello
$ ls -lh foo.jar
-rw-r--r-- 1 borkdude staff 871B Aug 19 17:07 foo.jar
2020-08-19 14:39:42 +00:00
```
2020-08-19 15:12:14 +00:00
If you want your uberjar to be compatible with the JVM, you'll need to compile
the main namespace. Babashka does not do compilation, so we use Clojure on the
JVM for that part:
2020-08-19 15:13:05 +00:00
``` clojure
2020-08-19 15:12:14 +00:00
$ rm foo.jar
$ mkdir classes
$ clojure -e "(require 'foo) (compile 'foo)"
foo
$ bb -cp $(clojure -Spath):classes -m foo --uberjar foo.jar
$ bb foo.jar
:hello
$ java -jar foo.jar
:hello
2020-08-19 14:39:42 +00:00
```
2020-06-26 08:43:49 +00:00
## System properties
Babashka sets the following system properties:
- `babashka.version` : the version string, e.g. `"1.2.0"`
- `babashka.main` : the `--main` argument
2020-08-14 09:39:48 +00:00
- `babashka.file` : the `--file` argument (normalized using `.getAbsolutePath` )
2020-06-27 11:37:33 +00:00
2020-07-13 16:29:24 +00:00
## `__name__ == "__main__"` pattern
2020-06-27 11:37:33 +00:00
In Python scripts there is a well-known pattern to check if the current file was
2020-06-27 11:39:06 +00:00
the file invoked from the command line, or loaded from another file: the
2020-07-13 16:29:24 +00:00
`__name__ == "__main__"` pattern. In babashka this pattern can be implemented with:
2020-06-27 11:39:06 +00:00
``` clojure
(= *file* (System/getProperty "babashka.file")
```
2020-06-26 08:43:49 +00:00
2020-05-16 13:42:33 +00:00
## Data readers
Data readers can be enabled by setting `*data-readers*` to a hashmap of symbols
to functions or vars:
``` clojure
$ bb "(set! *data-readers* {'t/tag inc}) #t/tag 1"
2
```
To preserve good startup time, babashka does not scan the classpath for
`data_readers.clj` files.
2019-09-11 21:37:25 +00:00
## Parsing command line arguments
Babashka ships with `clojure.tools.cli` :
``` clojure
(require '[clojure.tools.cli :refer [parse-opts]])
(def cli-options
;; An option with a required argument
[["-p" "--port PORT" "Port number"
:default 80
:parse-fn #(Integer/parseInt %)
:validate [#(< 0 % 0x10000 ) " Must be a number between 0 and 65536 " ] ]
["-h" "--help"]])
(:options (parse-opts *command-line-args* cli-options))
```
``` shellsession
$ bb script.clj
{:port 80}
$ bb script.clj -h
{:port 80, :help true}
```
2019-10-26 21:53:10 +00:00
## Reader conditionals
2020-02-19 22:58:49 +00:00
Babashka supports reader conditionals by taking either the `:bb` or `:clj`
branch, whichever comes first. NOTE: the `:clj` branch behavior was added in
version 0.0.71, before that version the `:clj` branch was ignored.
2019-10-26 21:53:10 +00:00
``` clojure
2020-02-19 22:58:49 +00:00
$ bb "#?(:bb :hello :clj :bye)"
:hello
$ bb "#?(:clj :bye :bb :hello)"
:bye
2019-10-26 21:53:10 +00:00
2020-02-19 22:58:49 +00:00
$ bb "[1 2 #?@(:bb [] :clj [1])]"
[1 2]
2019-10-26 21:53:10 +00:00
```
2020-01-30 10:08:12 +00:00
## Running tests
Babashka bundles `clojure.test` . To make CI scripts fail you can use a simple
runner like this:
``` shell
#!/usr/bin/env bash
bb -cp "src:test:resources" \
-e "(require '[clojure.test :as t] '[borkdude.deps-test])
(let [{:keys [:fail :error]} (t/run-tests 'borkdude.deps-test)]
(System/exit (+ fail error)))"
```
2019-09-04 12:37:07 +00:00
## Spawning and killing a process
2019-12-17 10:27:40 +00:00
Use the `java.lang.ProcessBuilder` class.
2019-09-04 12:37:07 +00:00
Example:
``` clojure
2019-12-17 10:27:40 +00:00
user=> (def ws (-> (ProcessBuilder. ["python" "-m" "SimpleHTTPServer" "1777"]) (.start)))
#'user/ws
user=> (wait/wait-for-port "localhost" 1777)
{:host "localhost", :port 1777, :took 2}
user=> (.destroy ws)
nil
2019-09-04 12:37:07 +00:00
```
2019-12-17 10:27:40 +00:00
Also see this [example ](examples/process_builder.clj ).
2020-08-31 12:59:57 +00:00
## Core.async
2019-09-07 08:43:53 +00:00
2020-03-01 21:45:55 +00:00
In addition to `future` , `pmap` , `promise` and friends, you may use the
`clojure.core.async` namespace for asynchronous scripting. The following example
shows how to get first available value from two different processes:
2019-09-07 08:43:53 +00:00
``` clojure
bb '
(defn async-command [& args]
(async/thread (apply shell/sh "bash" "-c" args)))
(-> (async/alts!! [(async-command "sleep 2 & & echo process 1")
(async-command "sleep 1 & & echo process 2")])
first :out str/trim println)'
process 2
```
2020-08-31 12:59:57 +00:00
Caveat: currently the `go` macro is available for compatibility with JVM
programs, but the implementation maps to `clojure.core.async/thread` and the
single exclamation mark operations (`< !`, `>!` , etc.) map to the double
exclamation mark operations (`< !!`, `>!!` , etc.). It will not "park" threads,
like on the JVM.
Examples like the following may still work, but will take a lot more system
resources than on the JVM and will break down for some high value of `n` :
``` clojure
(require '[clojure.core.async :as async])
(def n 1000)
(let [cs (repeatedly n async/chan)
begin (System/currentTimeMillis)]
(doseq [c cs] (async/go (async/>! c "hi")))
(dotimes [_ n]
(let [[v _] (async/alts!! cs)]
(assert (= "hi" v))))
(println "Read" n "msgs in" (- (System/currentTimeMillis) begin) "ms"))
```
2020-03-01 15:56:28 +00:00
2020-02-29 08:22:22 +00:00
## HTTP
For making HTTP requests you can use:
2020-03-07 13:10:58 +00:00
- [babashka.curl ](https://github.com/borkdude/babashka.curl ). This library is
included with babashka and aliased as `curl` in the user namespace.
2020-02-29 08:22:22 +00:00
- `slurp` for simple `GET` requests
2020-03-07 13:10:58 +00:00
- [clj-http-lite ](https://github.com/borkdude/clj-http-lite ) as a library.
2020-03-08 11:05:34 +00:00
- `clojure.java.shell` or `java.lang.ProcessBuilder` for shelling out to your
favorite command line http client
2020-02-29 08:22:22 +00:00
### HTTP over Unix sockets
This can be useful for talking to Docker:
``` clojure
(require '[clojure.java.shell :refer [sh]])
(require '[cheshire.core :as json])
(-> (sh "curl" "--silent"
"--no-buffer" "--unix-socket"
"/var/run/docker.sock"
"http://localhost/images/json")
:out
(json/parse-string true)
first
:RepoTags) ;;=> ["borkdude/babashka:latest"]
```
2020-04-08 19:18:56 +00:00
## Shutdown hook
Adding a shutdown hook allows you to execute some code before the script exits.
``` clojure
$ bb -e '(-> (Runtime/getRuntime) (.addShutdownHook (Thread. #(println "bye"))))'
bye
```
This also works when the script is interrupted with ctrl-c.
2020-04-27 09:55:58 +00:00
## JDBC
2020-04-30 21:17:29 +00:00
Babashka supports the [`next.jdbc` ](https://github.com/seancorfield/next-jdbc )
library along with drivers for [PostgresQL ](https://www.postgresql.org/ ) and
[HSQLDB ](http://hsqldb.org/ ). These features are not part of the standard `bb`
2020-09-01 21:50:28 +00:00
distribution but available via feature flags. See [doc/build.md ](doc/build.md )
for details on how to build babashka with these features. See this
2020-04-30 21:17:29 +00:00
[test ](test-resources/babashka/postgres_test.clj ) for an example how to use
this.
2020-04-27 09:55:58 +00:00
2020-04-30 21:17:29 +00:00
Interacting with `psql` , `mysql` and the `sqlite` CLIs can be achieved by
shelling out. See the [examples ](examples ) directory.
2020-04-27 09:55:58 +00:00
2020-09-01 21:50:28 +00:00
## Communicating with an nREPL server
2020-04-05 09:46:55 +00:00
Babashka comes with the [nrepl/bencode ](https://github.com/nrepl/bencode )
library which allows you to read and write bencode messages to a socket. A
2020-04-05 09:54:10 +00:00
simple example which evaluates a Clojure expression on an nREPL server started
with `lein repl` :
2020-04-05 09:46:55 +00:00
``` clojure
(ns nrepl-client
(:require [bencode.core :as b]))
2020-04-05 09:54:10 +00:00
(defn nrepl-eval [port expr]
(let [s (java.net.Socket. "localhost" port)
out (.getOutputStream s)
in (java.io.PushbackInputStream. (.getInputStream s))
2020-04-06 18:33:50 +00:00
_ (b/write-bencode out {"op" "eval" "code" expr})
bytes (get (b/read-bencode in) "value")]
(String. bytes)))
2020-04-05 09:54:10 +00:00
2020-04-06 18:33:50 +00:00
(nrepl-eval 52054 "(+ 1 2 3)") ;;=> "6"
2020-04-05 09:46:55 +00:00
```
2020-07-27 18:26:05 +00:00
## Printing returned values
Babashka doesn't print a returned `nil` as lots of scripts end in something side-effecting.
``` shell
$ bb '(:a {:a 5}'
5
$ bb '(:b {:a 5}'
$
```
2020-09-01 21:50:28 +00:00
2020-07-27 18:26:05 +00:00
If you really want to print the nil, you can use `(prn ..)` instead.
2019-10-07 21:11:49 +00:00
## Differences with Clojure
2019-10-07 21:09:35 +00:00
Babashka is implemented using the [Small Clojure
Interpreter](https://github.com/borkdude/sci). This means that a snippet or
script is not compiled to JVM bytecode, but executed form by form by a runtime
2020-03-02 08:50:47 +00:00
which implements a sufficiently large subset of Clojure. Babashka is compiled to
a native binary using [GraalVM ](https://github.com/oracle/graal ). It comes with
a selection of built-in namespaces and functions from Clojure and other useful
libraries. The data types (numbers, strings, persistent collections) are the
2019-10-07 21:09:35 +00:00
same. Multi-threading is supported (`pmap`, `future` ).
2019-10-07 21:11:49 +00:00
Differences with Clojure:
2019-10-07 21:09:35 +00:00
2020-03-02 08:50:47 +00:00
- A pre-selected set of Java classes are supported. You cannot add Java classes
at runtime.
2019-10-07 21:09:35 +00:00
2020-08-31 13:01:42 +00:00
- Interpretation comes with overhead. Therefore loops are slower than in Clojure
on the JVM. In general interpretation yields slower programs than compiled
programs.
2019-10-07 21:09:35 +00:00
2020-06-11 07:29:22 +00:00
- No `deftype` , `definterface` and unboxed math.
2019-10-07 21:09:35 +00:00
2020-08-31 12:59:57 +00:00
- `defprotocol` and `defrecord` are implemented using multimethods and regular
maps. Ostensibly they work the same, but under the hood there are no Java
classes that correspond to them.
- The `clojure.core.async/go` macro is not (yet) supported. For compatibility it
2020-08-31 13:02:15 +00:00
currently maps to `clojure.core.async/thread` . More info [here ](#coreasync ).
2020-08-31 12:59:57 +00:00
2020-05-28 09:52:18 +00:00
## [Libraries, pods and projects](doc/libraries.md)
2019-12-20 09:46:31 +00:00
2020-05-28 09:52:18 +00:00
A list of projects (scripts, libraries, pods and tools) known to work with babashka.
2020-04-21 10:06:43 +00:00
2020-05-20 11:56:00 +00:00
## Pods
2020-05-07 18:07:55 +00:00
2020-05-28 09:52:18 +00:00
Pods are programs that can be used as a Clojure library by
babashka. Documentation is available in the [library
repo](https://github.com/babashka/babashka.pods).
2020-05-07 18:07:55 +00:00
2020-03-28 08:52:55 +00:00
## Package babashka script as a AWS Lambda
AWS Lambda runtime doesn't support signals, therefore babashka has to disable
2020-06-08 19:27:40 +00:00
handling of SIGINT and SIGPIPE. This can be done by setting
`BABASHKA_DISABLE_SIGNAL_HANDLERS` to `true` .
2020-03-28 08:52:55 +00:00
2020-04-06 19:53:54 +00:00
## Articles, podcasts and videos
2019-12-20 09:45:07 +00:00
2020-09-01 22:01:33 +00:00
- [Using Clojure in Command Line with Babashka ](https://www.karimarttila.fi/clojure/2020/09/01/using-clojure-in-command-line-with-babashka.html ), a blog article by Kari Marttila.
2020-08-13 21:22:16 +00:00
- [Babashka and GraalVM; taking Clojure to new places ](https://youtu.be/3EUMA6bd-xQ ), a talk by Michiel Borkent at [Clojure/NYC ](https://www.meetup.com/Clojure-NYC/ ).
2020-07-13 19:47:48 +00:00
- [Import a CSV into Kafka, using Babashka ](https://blog.davemartin.me/posts/import-a-csv-into-kafka-using-babashka/ ) by Dave Martin
2020-09-01 21:50:28 +00:00
- [Learning about babashka ](https://amontalenti.com/2020/07/11/babashka ), a blog article by Andrew Montalenti
2020-05-19 09:26:44 +00:00
- [Babashka Pods ](https://www.youtube.com/watch?v=3Q4GUiUIrzg&feature=emb_logo ) presentation by Michiel Borkent at the [Dutch Clojure Meetup ](http://meetup.com/The-Dutch-Clojure-Meetup ).
2020-07-11 20:05:28 +00:00
- [AWS Logs using Babashka ](https://tech.toyokumo.co.jp/entry/aws_logs_babashka ), a blog published by [Toyokumo ](https://toyokumo.co.jp/ ).
2020-05-19 09:26:44 +00:00
- [The REPL podcast ](https://www.therepl.net/episodes/36/ ) Michiel Borkent talks about [clj-kondo ](https://github.com/borkdude/clj-kondo ), [Jet ](https://github.com/borkdude/jet ), Babashka, and [GraalVM ](https://github.com/oracle/graal ) with Daniel Compton.
2020-04-09 20:02:54 +00:00
- [Implementing an nREPL server for babashka ](https://youtu.be/0YmZYnwyHHc ): impromptu presentation by Michiel Borkent at the online [Dutch Clojure Meetup ](http://meetup.com/The-Dutch-Clojure-Meetup )
2020-04-06 19:53:11 +00:00
- [ClojureScript podcast ](https://soundcloud.com/user-959992602/s3-e5-babashka-with-michiel-borkent ) with Jacek Schae interviewing Michiel Borkent
- [Babashka talk at ClojureD ](https://www.youtube.com/watch?v=Nw8aN-nrdEk ) ([slides](https://speakerdeck.com/borkdude/babashka-and-the-small-clojure-interpreter-at-clojured-2020)) by Michiel Borkent
2020-02-21 11:15:47 +00:00
- [Babashka: a quick example ](https://juxt.pro/blog/posts/babashka.html ) by Malcolm Sparks
2019-12-21 20:05:40 +00:00
- [Clojure Start Time in 2019 ](https://stuartsierra.com/2019/12/21/clojure-start-time-in-2019 ) by Stuart Sierra
2019-12-20 09:45:07 +00:00
- [Advent of Random
Hacks](https://lambdaisland.com/blog/2019-12-19-advent-of-parens-19-advent-of-random-hacks)
by Arne Brasseur
- [Clojure in the Shell ](https://lambdaisland.com/blog/2019-12-05-advent-of-parens-5-clojure-in-the-shell ) by Arne Brasseur
- [Clojure Tool ](https://purelyfunctional.tv/issues/purelyfunctional-tv-newsletter-351-clojure-tool-babashka/ ) by Eric Normand
2020-04-26 11:10:30 +00:00
## [Building babashka](doc/build.md)
2020-01-08 20:30:50 +00:00
## [Developing Babashka](doc/dev.md)
2019-08-09 12:51:42 +00:00
2020-05-01 11:12:53 +00:00
## Including new libraries or classes
Before new libraries or classes go into the standardly distributed babashka
binary, these evaluation criteria are considered:
2020-05-01 13:40:53 +00:00
- The library or class is useful for general purpose scripting.
2020-05-01 11:12:53 +00:00
- Adding the library or class would make babashka more compatible with Clojure
2020-05-01 13:40:53 +00:00
libraries relevant to scripting.
2020-05-02 08:44:41 +00:00
- The library cannot be interpreted by with babashka using `--classpath` .
2020-05-01 11:12:53 +00:00
- The functionality can't be met by shelling out to another CLI or can't be
written as a small layer over an existing CLI (like `babashka.curl` ) instead.
2020-06-12 17:47:44 +00:00
- The library cannot be implemented a
[pod ](https://github.com/babashka/babashka.pods ).
2020-05-01 11:12:53 +00:00
If not all of the criteria are met, but adding a feature is still useful to a
particular company or niche, adding it behind a feature flag is still a
possibility. This is currently the case for `next.jdbc` and the `PostgresQL` and
`HSQLDB` database drivers. Companies interested in these features can compile an
instance of babashka for their internal use. Companies are also free to make
2020-05-01 11:16:38 +00:00
forks of babashka and include their own internal libraries. If their customized
2020-05-01 11:15:11 +00:00
babashka is interesting to share with the world, they are free to distribute it
using a different binary name (like `bb-sql` , `bb-docker` , `bb-yourcompany` ,
etc.). See the [feature flag documentation ](doc/build.md#feature-flags ) and the
implementation of the existing feature flags ([example
commit](https://github.com/borkdude/babashka/commit/02c7c51ad4b2b1ab9aa95c26a74448b138fe6659)).
2020-05-01 11:12:53 +00:00
2019-08-13 07:20:48 +00:00
## Related projects
- [planck ](https://planck-repl.org/ )
- [joker ](https://github.com/candid82/joker )
- [closh ](https://github.com/dundalek/closh )
- [lumo ](https://github.com/anmonteiro/lumo )
2020-04-22 10:01:22 +00:00
## [Examples](doc/examples.md)
2019-08-10 16:50:48 +00:00
2020-04-22 10:01:22 +00:00
A collection of example scripts.
2020-04-18 21:21:51 +00:00
2019-11-03 20:28:16 +00:00
## Thanks
2019-11-03 20:33:01 +00:00
- [adgoji ](https://www.adgoji.com/ ) for financial support
2020-04-05 09:32:20 +00:00
- [CircleCI ](https://circleci.com/ ) for CI and additional support
2020-02-23 22:20:16 +00:00
- [Nikita Prokopov ](https://github.com/tonsky ) for the logo
- [contributors ](https://github.com/borkdude/babashka/graphs/contributors ) and
other users posting issues with bug reports and ideas
2019-11-03 20:28:16 +00:00
2020-04-17 19:16:45 +00:00
## Contributors
### Code Contributors
2020-04-17 20:05:22 +00:00
This project exists thanks to all the people who contribute. [[Contribute ](doc/dev.md )].
2020-04-17 19:16:45 +00:00
< a href = "https://github.com/borkdude/babashka/graphs/contributors" > < img src = "https://opencollective.com/babashka/contributors.svg?width=890&button=false" / > < / a >
### Financial Contributors
Become a financial contributor and help us sustain our community. [[Contribute ](https://opencollective.com/babashka/contribute )]
#### Individuals
< a href = "https://opencollective.com/babashka" > < img src = "https://opencollective.com/babashka/individuals.svg?width=890" > < / a >
#### Organizations
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute ](https://opencollective.com/babashka/contribute )]
< a href = "https://opencollective.com/babashka/organization/0/website" > < img src = "https://opencollective.com/babashka/organization/0/avatar.svg" > < / a >
< a href = "https://opencollective.com/babashka/organization/1/website" > < img src = "https://opencollective.com/babashka/organization/1/avatar.svg" > < / a >
< a href = "https://opencollective.com/babashka/organization/2/website" > < img src = "https://opencollective.com/babashka/organization/2/avatar.svg" > < / a >
< a href = "https://opencollective.com/babashka/organization/3/website" > < img src = "https://opencollective.com/babashka/organization/3/avatar.svg" > < / a >
< a href = "https://opencollective.com/babashka/organization/4/website" > < img src = "https://opencollective.com/babashka/organization/4/avatar.svg" > < / a >
< a href = "https://opencollective.com/babashka/organization/5/website" > < img src = "https://opencollective.com/babashka/organization/5/avatar.svg" > < / a >
< a href = "https://opencollective.com/babashka/organization/6/website" > < img src = "https://opencollective.com/babashka/organization/6/avatar.svg" > < / a >
< a href = "https://opencollective.com/babashka/organization/7/website" > < img src = "https://opencollective.com/babashka/organization/7/avatar.svg" > < / a >
< a href = "https://opencollective.com/babashka/organization/8/website" > < img src = "https://opencollective.com/babashka/organization/8/avatar.svg" > < / a >
< a href = "https://opencollective.com/babashka/organization/9/website" > < img src = "https://opencollective.com/babashka/organization/9/avatar.svg" > < / a >
2019-08-09 12:51:42 +00:00
## License
2020-03-02 13:21:57 +00:00
Copyright © 2019-2020 Michiel Borkent
2019-08-09 12:51:42 +00:00
2019-09-04 12:37:07 +00:00
Distributed under the EPL License. See LICENSE.
This project contains code from:
- Clojure, which is licensed under the same EPL License.