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)
[](https://clojars.org/borkdude/babashka)
2019-12-12 11:18:24 +00:00
[](https://app.slack.com/client/T03RZGPFR/CLX41ASCS)
2019-12-19 13:01:14 +00:00
<!-- [](https://cljdoc.org/d/borkdude/babashka/CURRENT) -->
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-02 08:50:47 +00:00
The sweet spot for babashka is executing Clojure expressions or scripts in the
same space where you would use Bash.
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-02 09:01:04 +00:00
## Goals
2019-11-03 20:28:16 +00:00
2020-03-02 08:50:47 +00:00
* Low latency Clojure scripting alternative to JVM Clojure.
2020-03-02 08:55:50 +00:00
* Easy installation: grab the self-contained binary and run. No JVM needed.
2020-03-02 08:50:47 +00:00
* Familiarity and portability:
- Scripts should be compatible with JVM Clojure as much as possible
2020-03-02 08:55:50 +00:00
- Scripts should be platform-independent as much as possible. Babashka offers
support for linux, macOS and Windows.
2020-03-02 08:50:47 +00:00
* Allow interop with commonly used classes like `java.io.File` and `System`
* Multi-threading support (`pmap`, `future` , `core.async` )
* Batteries included (tools.cli, cheshire, ...)
* Library support via popular tools like the `clojure` CLI
2019-11-03 20:28:16 +00:00
2020-03-02 09:02:36 +00:00
Also see the [slides ](https://speakerdeck.com/borkdude/babashka-and-the-small-clojure-interpreter-at-clojured-2020 ) of the Babashka talk at ClojureD 2020 (video coming soon).
2020-03-02 09:01:04 +00:00
## Non-goals
2019-11-03 20:28:16 +00:00
2020-03-02 08:56:22 +00:00
* Performance< sup > 1< sup >
* 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.
< sup > 1< sup > Babashka uses [sci ](https://github.com/borkdude/sci ) for
2020-03-02 08:55:50 +00:00
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, Clojure on the JVM
2020-03-02 08:57:24 +00:00
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
2020-03-02 08:55:50 +00:00
[here ](#differences-with-clojure ).
2019-08-17 22:31:39 +00:00
2020-03-02 09:00:28 +00:00
## Quickstart
``` shellsession
$ bash < (curl -s https://raw.githubusercontent.com/borkdude/babashka/master/install)
$ ls | bb --time -i '(filter #(-> % io/file .isDirectory) *input* )'
("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
```
More examples can be found in the [gallery ](#gallery ).
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
[CHANGES.md ](CHANGES.md ) before upgrading.
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
$ bash < (curl -s https://raw.githubusercontent.com/borkdude/babashka/master/install)
```
By default this will install into `/usr/local/bin` . To change this, provide the directory name:
``` shellsession
$ bash < (curl -s https://raw.githubusercontent.com/borkdude/babashka/master/install) /tmp
```
### 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
2019-12-13 17:16:13 +00:00
Usage: bb [ -i | -I ] [ -o | -O ] [ --stream ] [--verbose]
2020-01-01 22:27:25 +00:00
[ ( --classpath | -cp ) < cp > ] [ --uberscript < file > ]
[ ( --main | -m ) < main-namespace > | -e < expression > | -f < file > |
--repl | --socket-repl [< host > :]< port > ]
[ arg* ]
2019-08-18 10:33:54 +00:00
Options:
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-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.
--verbose Print entire stacktrace in case of exception.
--stream Stream over lines or EDN values from stdin. Combined with -i or -I *input* becomes a single value per iteration.
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.
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-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).
--time Print execution time before exiting.
2020-01-31 16:34:39 +00:00
-- Stop parsing args and pass everything after -- to *command-line-args*
2019-09-06 14:40:50 +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* .
2019-08-18 10:33:54 +00:00
```
2019-08-14 11:38:39 +00:00
The `clojure.core` functions are accessible without a namespace alias.
2019-09-08 21:31:24 +00:00
The following namespaces are required by default and available through the
2019-12-10 08:44:41 +00:00
pre-defined aliases in the `user` namespace. You may use `require` + `:as`
and/or `:refer` on these namespaces. If not all vars are available, they are
enumerated explicitly.
2019-08-14 11:38:39 +00:00
- `clojure.string` aliased as `str`
- `clojure.set` aliased as `set`
2019-08-18 10:33:54 +00:00
- `clojure.edn` aliased as `edn` :
- `read-string`
2020-01-25 16:00:40 +00:00
- `clojure.java.shell` aliased as `shell`
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`
2019-12-24 09:01:32 +00:00
- `clojure.main` : `repl`
2019-09-07 08:43:53 +00:00
- [`clojure.core.async` ](https://clojure.github.io/core.async/ ) aliased as
2020-03-01 15:56:28 +00:00
`async` .
2020-01-02 12:53:35 +00:00
- `clojure.stacktrace`
2020-01-29 22:53:01 +00:00
- `clojure.test`
2020-02-19 22:22:49 +00:00
- `clojure.pprint` : `pprint` (currently backed by [fipp ](https://github.com/brandonbloom/fipp )'s `fipp.edn/pprint` )
2019-09-08 21:07:58 +00:00
- [`clojure.tools.cli` ](https://github.com/clojure/tools.cli ) aliased as `tools.cli`
2019-11-11 20:14:30 +00:00
- [`clojure.data.csv` ](https://github.com/clojure/data.csv ) aliased as `csv`
2019-12-10 08:44:41 +00:00
- [`cheshire.core` ](https://github.com/dakrone/cheshire ) aliased as `json`
2020-03-20 16:16:42 +00:00
- [`cognitect.transit` ](https://github.com/cognitect/transit-clj ) aliased as `transit`
2019-08-14 11:38:39 +00:00
2020-01-04 19:06:11 +00:00
A selection of java classes are available, see `babashka/impl/classes.clj` .
2019-12-31 13:23:54 +00:00
Babashka supports `import` : `(import clojure.lang.ExceptionInfo)` .
2019-11-27 13:15:29 +00:00
Babashka supports a subset of the `ns` form where you may use `:require` and `:import` :
``` shellsession
(ns foo
(:require [clojure.string :as str])
(:import clojure.lang.ExceptionInfo))
2019-11-15 23:25:36 +00:00
```
2019-11-27 13:15:29 +00:00
For the unsupported parts of the ns form, you may use [reader
conditionals](#reader-conditionals) to maintain compatibility with JVM Clojure.
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
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
Command-line arguments can be retrieved using `*command-line-args*` .
2020-01-12 19:41:53 +00:00
### Additional 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]]
'[clojure.java.shell :refer [sh]])
2020-01-12 19:41:53 +00:00
(def medley-dep '{:deps {medley {:git/url "https://github.com/borkdude/medley"
:sha "91adfb5da33f8d23f75f0894da1defe567a625c0"}}})
2020-01-12 19:46:00 +00:00
(def cp (:out (sh "clojure" "-Spath" "-Sdeps" (str medley-dep))))
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 ).
2019-08-14 11:38:39 +00:00
## Running a file
2019-08-15 10:55:58 +00:00
Scripts may be executed from a file using `-f` or `--file` :
2019-08-14 11:38:39 +00:00
``` shellsession
2019-08-15 07:08:30 +00:00
bb -f download_html.clj
2019-08-14 11:38:39 +00:00
```
2019-08-18 05:34:47 +00:00
Files can also be loaded inline using `load-file` :
``` shellsession
bb '(load-file "script.clj")'
```
2019-08-14 11:38:39 +00:00
Using `bb` with a shebang also works:
2019-08-15 07:04:14 +00:00
``` clojure
2019-09-07 08:49:31 +00:00
#!/usr/bin/env bb
2019-08-15 04:28:00 +00:00
(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]
2019-08-15 07:08:30 +00:00
(println "Writing file:" file)
2019-08-15 04:28:00 +00:00
(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)))
(System/exit 0)
2019-08-14 11:38:39 +00:00
```
2019-08-15 08:41:37 +00:00
``` 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
```
2019-09-07 08:49:31 +00:00
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-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
namespaces and load them:
``` 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!
```
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-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-01-02 13:31:13 +00:00
### Deps.clj
2020-01-02 13:34:26 +00:00
The [`deps.clj` ](https://github.com/borkdude/deps.clj/ ) script can be used to work with `deps.edn` -based projects:
2019-12-28 21:52:56 +00:00
``` shell
2019-12-28 22:06:48 +00:00
$ deps.clj -A:my-script -Scommand "bb -cp {{classpath}} {{main-opts}}"
2019-12-28 21:52:56 +00:00
Hello from gist script!
```
2020-01-02 13:41:59 +00:00
Create these aliases for brevity:
2020-01-02 13:31:13 +00:00
``` shell
2020-01-02 13:41:59 +00:00
$ alias bbk='deps.clj -Scommand "bb -cp {{classpath}} {{main-opts}}"'
2020-01-02 13:45:41 +00:00
$ alias babashka='rlwrap deps.clj -Scommand "bb -cp {{classpath}} {{main-opts}}"'
2020-01-02 13:41:59 +00:00
$ bbk -A:my-script
2020-01-02 13:31:13 +00:00
Hello from gist script!
2020-01-02 13:41:59 +00:00
$ babashka
Babashka v0.0.58 REPL.
Use :repl/quit or :repl/exit to quit the REPL.
Clojure rocks, Bash reaches.
user=> (require '[my-gist-script :as mgs])
nil
user=> (mgs/-main)
Hello from gist script!
nil
2020-01-02 13:31:13 +00:00
```
2019-12-28 21:52:56 +00:00
## Uberscript
2019-12-28 21:55:01 +00:00
The `--uberscript` option collects the expressions in
2019-12-28 21:52:56 +00:00
`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
2019-12-28 22:50:05 +00:00
convenient for debugging and deployment.
2019-12-28 21:52:56 +00:00
Given the `deps.edn` from above:
2019-12-28 22:04:38 +00:00
``` clojure
2019-12-28 22:06:48 +00:00
$ deps.clj -A:my-script -Scommand "bb -cp {{classpath}} {{main-opts}} --uberscript my-script.clj"
2019-12-28 21:52:56 +00:00
$ cat my-script.clj
(ns my-gist-script)
(defn -main [& args]
2019-12-28 22:11:25 +00:00
(println "Hello from gist script!"))
2019-12-28 23:15:07 +00:00
(ns user (:require [my-gist-script]))
(apply my-gist-script/-main *command-line-args* )
2019-12-28 21:52:56 +00:00
$ bb my-script.clj
Hello from gist script!
```
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)))"
```
2020-02-07 08:33:02 +00:00
## REPL
2019-08-31 18:17:36 +00:00
2020-02-07 08:33:02 +00:00
Babashka supports both a REPL and socket REPL. To start the REPL, type:
``` shell
$ bb --repl
```
To get history with up and down arrows, use `rlwrap` :
``` shell
$ rlwrap bb --repl
```
To start the socket REPL you can do this:
2019-08-31 18:17:36 +00:00
``` shellsession
$ bb --socket-repl 1666
Babashka socket REPL started at localhost:1666
```
Now you can connect with your favorite socket REPL client:
``` shellsession
$ rlwrap nc 127.0.0.1 1666
2019-08-31 19:22:31 +00:00
Babashka v0.0.14 REPL.
2019-08-31 18:17:36 +00:00
Use :repl/quit or :repl/exit to quit the REPL.
Clojure rocks, Bash reaches.
bb=> (+ 1 2 3)
6
bb=> :repl/quit
$
```
2020-03-12 10:01:45 +00:00
Editor plugins offering auto-completion support when connected to a babashka socket REPL:
2020-03-20 14:47:54 +00:00
- Emacs: [inf-clojure ](https://github.com/clojure-emacs/inf-clojure ):
2020-03-20 14:52:30 +00:00
2020-03-20 14:47:54 +00:00
To connect:
2020-03-20 14:52:30 +00:00
2020-03-20 14:47:54 +00:00
`M-x inf-clojure-connect localhost 1666`
2020-03-20 14:52:30 +00:00
2020-03-20 14:47:54 +00:00
Before evaluating from a Clojure buffer:
2020-03-20 14:52:30 +00:00
2020-03-20 14:47:54 +00:00
`M-x inf-clojure-minor-mode`
2020-03-12 10:01:45 +00:00
- Atom: [chlorine ](https://github.com/mauricioszabo/atom-chlorine )
- Vim: [vim-iced ](https://github.com/liquidz/vim-iced )
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 ).
2019-09-07 08:43:53 +00:00
## Async
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-03-01 15:56:28 +00:00
Note: the `go` macro is available for compatibility with JVM programs, but the
implementation maps to `clojure.core.async/thread` and the single exclamation
2020-03-01 16:43:10 +00:00
mark operations (`< !`, `>!` , etc.) map to the double exclamation mark operations
2020-03-01 21:45:55 +00:00
(`< !!`, `>!!` , etc.). It will not "park" threads, like on the JVM.
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"]
```
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
- Interpretation comes with overhead. Therefore tight loops are likely slower
2020-03-02 08:50:47 +00:00
than in Clojure on the JVM. In general interpretation yields slower programs
than compiled programs.
2019-10-07 21:09:35 +00:00
- No support for unboxed types.
2019-12-20 09:46:31 +00:00
## External resources
2019-12-25 13:49:29 +00:00
### Tools and libraries
2019-12-24 10:31:31 +00:00
The following libraries are known to work with Babashka:
2019-12-25 13:49:29 +00:00
#### [deps.clj](https://github.com/borkdude/deps.clj)
A port of the [clojure ](https://github.com/clojure/brew-install/ ) bash script to
2019-12-29 13:56:16 +00:00
Clojure / babashka.
2020-01-09 13:58:03 +00:00
#### [spartan.spec](https://github.com/borkdude/spartan.spec/)
An babashka-compatible implementation of `clojure.spec.alpha` .
2020-01-29 22:53:01 +00:00
#### [missing.test.assertions](https://github.com/borkdude/missing.test.assertions)
This library checks if no assertions have been made in a test:
``` shell
$ export BABASHKA_CLASSPATH=$(clojure -Spath -Sdeps '{:deps {borkdude/missing.test.assertions {:git/url "https://github.com/borkdude/missing.test.assertions" :sha "603cb01bee72fb17addacc53c34c85612684ad70"}}}')
$ lein bb "(require '[missing.test.assertions] '[clojure.test :as t]) (t/deftest foo) (t/run-tests)"
Testing user
WARNING: no assertions made in test foo
Ran 1 tests containing 0 assertions.
0 failures, 0 errors.
{:test 1, :pass 0, :fail 0, :error 0, :type :summary}
```
2020-02-20 09:56:07 +00:00
#### [medley](https://github.com/weavejester/medley/)
2020-01-02 12:42:59 +00:00
2020-02-20 09:56:07 +00:00
Requires `bb` >= v0.0.71. Latest coordinates checked with with bb:
``` clojure
{:git/url "https://github.com/weavejester" :sha "a4e5fb5383f5c0d83cb2d005181a35b76d8a136d"}
```
Example:
``` shell
$ export BABASHKA_CLASSPATH=$(clojure -Spath -Sdeps '{:deps {medley {:git/url "https://github.com/weavejester" :sha "a4e5fb5383f5c0d83cb2d005181a35b76d8a136d"}}}')
$ bb -e "(require '[medley.core :as m]) (m/index-by :id [{:id 1} {:id 2}])"
{1 {:id 1}, 2 {:id 2}}
```
2020-01-02 12:42:59 +00:00
2019-12-24 10:31:31 +00:00
#### [clj-http-lite](https://github.com/borkdude/clj-http-lite)
2019-12-25 13:49:29 +00:00
This fork does not depend on any other libraries. Example:
2019-12-24 10:31:31 +00:00
``` shell
2020-01-08 07:45:41 +00:00
$ export BABASHKA_CLASSPATH="$(clojure -Sdeps '{:deps {clj-http-lite {:git/url "https://github.com/borkdude/clj-http-lite" :sha "f44ebe45446f0f44f2b73761d102af3da6d0a13e"}}}' -Spath)"
2019-12-24 10:31:31 +00:00
$ bb "(require '[clj-http.lite.client :as client]) (:status (client/get \"https://www.clojure.org\"))"
200
```
#### [limit-break](https://github.com/technomancy/limit-break)
2020-02-20 09:56:07 +00:00
A debug REPL library.
Latest coordinates checked with with bb:
``` clojure
{:git/url "https://github.com/technomancy/limit-break" :sha "050fcfa0ea29fe3340927533a6fa6fffe23bfc2f" :deps/manifest :deps}
```
Example:
2019-12-24 10:31:31 +00:00
``` shell
$ export BABASHKA_CLASSPATH="$(clojure -Sdeps '{:deps {limit-break {:git/url "https://github.com/technomancy/limit-break" :sha "050fcfa0ea29fe3340927533a6fa6fffe23bfc2f" :deps/manifest :deps}}}' -Spath)"
$ bb "(require '[limit.break :as lb]) (let [x 1] (lb/break))"
Babashka v0.0.49 REPL.
Use :repl/quit or :repl/exit to quit the REPL.
Clojure rocks, Bash reaches.
break> x
1
```
2020-01-21 14:53:04 +00:00
#### [clojure-csv](https://github.com/davidsantiago/clojure-csv)
A library for reading and writing CSV files. Note that babashka already comes
with `clojure.data.csv` , but in case you need this other library, this is how
you can use it:
``` shell
export BABASHKA_CLASSPATH="$(clojure -Sdeps '{:deps {clojure-csv {:mvn/version "RELEASE"}}}' -Spath)"
./bb -e "
(require '[clojure-csv.core :as csv])
(csv/write-csv (csv/parse-csv \"a,b,c\n1,2,3\"))
"
```
2020-02-20 10:06:03 +00:00
#### [regal](https://github.com/lambdaisland/regal)
2020-02-17 11:09:34 +00:00
2020-02-20 09:56:07 +00:00
Requires `bb` >= v0.0.71. Latest coordinates checked with with bb:
``` clojure
2020-03-03 17:26:34 +00:00
{:git/url "https://github.com/lambdaisland/regal" :sha "d4e25e186f7b9705ebb3df6b21c90714d278efb7"}
2020-02-20 09:56:07 +00:00
```
Example:
``` shell
2020-03-03 17:26:34 +00:00
$ export BABASHKA_CLASSPATH=$(clojure -Spath -Sdeps '{:deps {regal {:git/url "https://github.com/lambdaisland/regal" :sha "d4e25e186f7b9705ebb3df6b21c90714d278efb7"}}}')
2020-02-20 09:56:07 +00:00
$ bb -e "(require '[lambdaisland.regal :as regal]) (regal/regex [:* \"ab\"])"
#"(?:\Qab\E)*"
```
2020-02-17 11:09:34 +00:00
2020-03-08 11:05:34 +00:00
#### [4bb](https://github.com/porkostomus/4bb)
2020-01-29 22:53:01 +00:00
2020-03-08 11:05:34 +00:00
4clojure as a babashka script!
2020-01-29 22:53:01 +00:00
2020-03-20 13:37:42 +00:00
#### [cprop](https://github.com/tolitius/cprop/)
A clojure configuration libary. Latest test version: `"0.1.16"` .
2020-03-21 16:27:41 +00:00
#### [comb](https://github.com/weavejester/comb)
Simple templating system for Clojure. Latest tested version: `"0.1.1"` .
``` clojure
$ export BABASHKA_CLASSPATH=$(clojure -Spath -Sdeps '{:deps {comb {:mvn/version "0.1.1"}}}')
$ rlwrap bb
...
user=> (require '[comb.template :as template])
user=> (template/eval "< % (dotimes [x 3] %>foo< % ) %>")
"foofoofoo"
user=> (template/eval "Hello < %= name %>" {:name "Alice"})
"Hello Alice"
user=> (def hello (template/fn [name] "Hello < %= name %>"))
user=> (hello "Alice")
"Hello Alice"
```
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
handling of the SIGPIPE. This can be done by setting
`BABASHKA_DISABLE_PIPE_SIGNAL_HANDLER` to `true` .
2019-12-20 09:46:31 +00:00
### Blogs
2019-12-20 09:45:07 +00:00
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-01-08 20:30:50 +00:00
## [Developing Babashka](doc/dev.md)
2019-08-09 12:51:42 +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 )
2019-08-10 16:50:48 +00:00
## Gallery
Here's a gallery of more useful examples. Do you have a useful example? PR
welcome!
2019-11-08 19:23:43 +00:00
### Delete a list of files returned by a Unix command
```
2019-12-20 22:51:24 +00:00
find . | grep conflict | bb -i '(doseq [f *input* ] (.delete (io/file f)))'
2019-11-08 19:23:43 +00:00
```
2019-11-27 21:55:21 +00:00
### Calculate aggregate size of directory
``` clojure
#!/usr/bin/env bb
(as-> (io/file (or (first *command-line-args* ) ".")) $
(file-seq $)
(map #(.length %) $)
(reduce + $)
(/ $ (* 1024 1024))
(println (str (int $) "M")))
```
``` shellsession
$ dir-size
130M
$ dir-size ~/Dropbox/bin
233M
```
2019-08-10 20:52:30 +00:00
### Shuffle the lines of a file
``` shellsession
$ cat /tmp/test.txt
1 Hello
2 Clojure
2019-08-13 11:43:15 +00:00
3 Babashka
2019-08-10 20:52:30 +00:00
4 Goodbye
2019-12-20 22:51:24 +00:00
$ < /tmp/test.txt bb -io '(shuffle *input* )'
2019-08-13 11:43:15 +00:00
3 Babashka
2019-08-10 20:52:30 +00:00
2 Clojure
4 Goodbye
1 Hello
```
2019-08-10 16:50:48 +00:00
### Fetch latest Github release tag
2020-03-01 11:06:19 +00:00
``` shell
(require '[clojure.java.shell :refer [sh]]
'[cheshire.core :as json])
2019-08-10 16:50:48 +00:00
2020-03-01 11:06:19 +00:00
(defn babashka-latest-version []
(-> (sh "curl" "https://api.github.com/repos/borkdude/babashka/tags")
:out
(json/parse-string true)
first
:name))
(babashka-latest-version) ;;=> "v0.0.73"
2019-08-10 18:34:24 +00:00
```
2020-02-02 12:14:29 +00:00
### Generate deps.edn entry for a gitlib
2019-08-10 18:34:24 +00:00
2020-02-02 12:14:29 +00:00
``` clojure
#!/usr/bin/env bb
(require '[clojure.java.shell :refer [sh]]
'[clojure.string :as str])
(let [[username project branch] *command-line-args*
branch (or branch "master")
url (str "https://github.com/" username "/" project)
sha (-> (sh "git" "ls-remote" url branch)
:out
(str/split #"\s")
first)]
{:git/url url
:sha sha})
```
``` shell
$ gitlib.clj nate fs
{:git/url "https://github.com/nate/fs", :sha "75b9fcd399ac37cb4f9752a4c7a6755f3fbbc000"}
2020-02-02 12:33:26 +00:00
$ clj -Sdeps "{:deps {fs $(gitlib.clj nate fs)}}" \
-e "(require '[nate.fs :as fs]) (fs/creation-time \".\")"
2020-02-02 12:29:50 +00:00
#object[java.nio.file.attribute.FileTime 0x5c748168 "2019-07-05T14:06:26Z"]
2019-08-10 16:50:48 +00:00
```
2019-11-03 20:38:06 +00:00
### View download statistics from Clojars
2019-12-18 07:53:32 +00:00
Contributed by [@plexus ](https://github.com/plexus ).
2019-11-03 20:38:06 +00:00
``` shellsession
2019-11-03 21:43:38 +00:00
$ curl https://clojars.org/stats/all.edn |
2019-12-20 22:51:24 +00:00
bb -o '(for [[[group art] counts] *input* ] (str (reduce + (vals counts)) " " group "/" art))' |
2019-11-03 20:39:36 +00:00
sort -rn |
less
2019-11-03 21:43:01 +00:00
14113842 clojure-complete/clojure-complete
9065525 clj-time/clj-time
8504122 cheshire/cheshire
...
2019-11-03 20:38:06 +00:00
```
2019-11-27 17:08:03 +00:00
### Portable tree command
2019-12-13 19:59:09 +00:00
See [examples/tree.clj ](https://github.com/borkdude/babashka/blob/master/examples/tree.clj ).
2019-11-27 17:20:50 +00:00
2019-11-27 17:08:03 +00:00
``` shellsession
$ clojure -Sdeps '{:deps {org.clojure/tools.cli {:mvn/version "0.4.2"}}}' examples/tree.clj src
src
└── babashka
├── impl
│ ├── tools
│ │ └── cli.clj
...
$ examples/tree.clj src
src
└── babashka
├── impl
│ ├── tools
│ │ └── cli.clj
2019-12-01 12:50:32 +00:00
...
2019-11-27 17:08:03 +00:00
```
2019-12-13 19:59:09 +00:00
### List outdated maven dependencies
See [examples/outdated.clj ](https://github.com/borkdude/babashka/blob/master/examples/outdated.clj ).
2019-12-13 20:00:20 +00:00
Inspired by an idea from [@seancorfield ](https://github.com/seancorfield ).
2019-12-13 19:59:09 +00:00
``` shellsession
$ cat /tmp/deps.edn
{:deps {cheshire {:mvn/version "5.8.1"}
clj-http {:mvn/version "3.4.0"}}}
$ examples/outdated.clj /tmp/deps.edn
clj-http/clj-http can be upgraded from 3.4.0 to 3.10.0
cheshire/cheshire can be upgraded from 5.8.1 to 5.9.0
```
2019-12-17 14:35:03 +00:00
### Convert project.clj to deps.edn
2019-12-18 07:52:14 +00:00
Contributed by [@plexus ](https://github.com/plexus ).
2019-12-18 07:51:40 +00:00
2019-12-17 14:37:05 +00:00
``` shellsession
$ cat project.clj |
sed -e 's/#=//g' -e 's/~@//g' -e 's/~//g' |
2019-12-20 22:51:24 +00:00
bb '(let [{:keys [dependencies source-paths resource-paths]} (apply hash-map (drop 3 *input* ))]
2019-12-17 14:37:05 +00:00
{:paths (into source-paths resource-paths)
:deps (into {} (for [[d v] dependencies] [d {:mvn/version v}]))}) ' |
jet --pretty > deps.edn
2019-12-17 14:35:03 +00:00
```
2019-12-18 15:38:21 +00:00
### Print current time in California
See [examples/pst.clj ](https://github.com/borkdude/babashka/blob/master/examples/pst.clj )
2020-01-06 12:22:43 +00:00
### Tiny http server
See [examples/http_server.clj ](https://github.com/borkdude/babashka/blob/master/examples/http_server.clj )
Original by [@souenzzo ](https://gist.github.com/souenzzo/a959a4c5b8c0c90df76fe33bb7dfe201 )
2020-02-20 11:20:55 +00:00
### Print random docstring
2020-02-20 11:45:44 +00:00
See [examples/random_doc.clj ](https://github.com/borkdude/babashka/blob/master/examples/random_doc.clj )
2020-02-20 11:20:55 +00:00
``` shell
2020-02-20 11:45:44 +00:00
$ examples/random_doc.clj
2020-02-20 11:20:55 +00:00
-------------------------
2020-02-20 11:45:44 +00:00
clojure.core/ffirst
2020-02-20 11:20:55 +00:00
([x])
2020-02-20 11:45:44 +00:00
Same as (first (first x))
2020-02-20 11:20:55 +00:00
```
2020-03-21 10:49:45 +00:00
### Cryptographic hash
2020-03-21 10:41:26 +00:00
2020-03-21 10:48:06 +00:00
`sha1.clj` :
2020-03-21 10:43:14 +00:00
``` clojure
2020-03-21 10:48:06 +00:00
#!/usr/bin/env bb
(defn sha1
[s]
(let [hashed (.digest (.getInstance java.security.MessageDigest "SHA-1")
(.getBytes s))
sw (java.io.StringWriter.)]
(binding [*out* sw]
(doseq [byte hashed]
(print (format "%02X" byte))))
(str sw)))
(sha1 (first *command-line-args* ))
2020-03-21 10:41:26 +00:00
```
``` shell
2020-03-21 10:50:40 +00:00
$ sha1.clj babashka
2020-03-21 10:48:06 +00:00
"0AB318BE3A646EEB1E592781CBFE4AE59701EDDF"
2020-03-21 10:41:26 +00:00
```
2020-03-21 13:37:29 +00:00
### Package script as Docker image
`Dockerfile` :
``` dockerfile
FROM borkdude/babashka
RUN echo $'\
(println "Your command line args:" *command-line-args* )\
'\
>> script.clj
ENTRYPOINT ["bb", "script.clj"]
```
``` shell
$ docker build . -t script
...
$ docker run --rm script 1 2 3
Your command line args: (1 2 3)
```
2020-03-28 08:52:55 +00:00
### Extract single file from zip
2020-03-21 19:17:57 +00:00
2020-03-28 08:52:55 +00:00
``` clojure
2020-03-28 08:55:36 +00:00
;; Given the following:
2020-03-28 08:52:55 +00:00
;; $ echo 'contents' > file
;; $ zip zipfile.zip file
;; $ rm file
2020-03-28 08:55:36 +00:00
;; we extract the single file from the zip archive using java.nio:
2020-03-28 08:52:55 +00:00
(import '[java.nio.file Files FileSystems CopyOption])
(let [zip-file (io/file "zipfile.zip")
file (io/file "file")
fs (FileSystems/newFileSystem (.toPath zip-file) nil)
file-in-zip (.getPath fs "file" (into-array String []))]
(Files/copy file-in-zip (.toPath file)
(into-array CopyOption [])))
```
2020-03-21 19:17:57 +00:00
2020-03-28 13:40:45 +00:00
### Note taking app
2020-03-28 13:36:53 +00:00
See
[examples/notes.clj ](https://github.com/borkdude/babashka/blob/master/examples/notes.clj ). This
is a variation on the
[http-server ](https://github.com/borkdude/babashka/#tiny-http-server ) example.
2020-03-28 13:40:45 +00:00
< img src = "assets/notes-example.png" width = "400px" >
2020-03-28 13:39:49 +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-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
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.