babashka/README.md

316 lines
7.7 KiB
Markdown
Raw Normal View History

2019-08-13 11:43:15 +00:00
# babashka
2019-08-09 12:51:42 +00:00
[![CircleCI](https://circleci.com/gh/borkdude/babashka/tree/master.svg?style=shield)](https://circleci.com/gh/borkdude/babashka/tree/master)
[![Clojars Project](https://img.shields.io/clojars/v/borkdude/babashka.svg)](https://clojars.org/borkdude/babashka)
[![cljdoc badge](https://cljdoc.org/badge/borkdude/babashka)](https://cljdoc.org/d/borkdude/babashka/CURRENT)
2019-08-12 12:42:45 +00:00
A sprinkle of Clojure for the command line.
## Quickstart
``` shellsession
$ bash <(curl -s https://raw.githubusercontent.com/borkdude/babashka/master/install)
$ bb '(vec (dedupe *in*))' <<< '[1 1 1 1 2]'
[1 2]
```
## Rationale
If you're a bash expert, you probably don't need this. But for those of us who
2019-08-15 09:18:25 +00:00
can use a bit of Clojure in their shell scripts, it may be useful.
2019-08-09 12:51:42 +00:00
2019-08-17 22:31:39 +00:00
Babashka runs as a binary which results in faster startup times:
2019-08-15 09:16:15 +00:00
2019-08-15 09:05:59 +00:00
``` shellsession
2019-08-17 22:31:39 +00:00
$ time clojure -e "(+ 1 2 3)"
6
clojure -e "(+ 1 2 3)" 3.29s user 0.32s system 99% cpu 3.638 total
2019-08-15 09:05:59 +00:00
2019-08-17 22:40:01 +00:00
$ time planck -e '(+ 1 2 3)'
2019-08-17 22:31:39 +00:00
6
plk -e '(+ 1 2 3)' 1.34s user 0.16s system 127% cpu 1.172 total
$ time bb '(+ 1 2 3)'
6
bb '(+ 1 2 3)' 0.01s user 0.01s system 37% cpu 0.046 total
2019-08-15 09:05:59 +00:00
```
2019-08-17 22:31:39 +00:00
It uses [sci](https://github.com/borkdude/sci) for interpreting Clojure. A
trade-off is that [sci](https://github.com/borkdude/sci) implements only a
subset of Clojure. Also, execution time may be slower than Clojure on the JVM or
(self-hosted) ClojureScript for more CPU-intensive calculations like:
``` shellsession
(last (take 1000000 (repeatedly #(+ 1 2 3))))
```
This would take 5 seconds using babashka, around half a second using self-hosted
ClojureScript and around 200ms in Clojure on the JVM.
So the sweet spot for babashka is executing tasks from the command line where
fast startup time is preferred, in the same space where you would use bash.
2019-08-15 09:16:15 +00:00
2019-08-09 12:51:42 +00:00
## Status
Experimental. Breaking changes are expected to happen at this phase.
2019-08-09 12:51:42 +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-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
2019-08-09 14:02:08 +00:00
You may also download a binary from [Github](https://github.com/borkdude/babashka/releases).
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-08-16 20:22:58 +00:00
bb [ --help ] | [ --version ] | ( [ -i ] [ -o ] | [ -io ] ) [ --stream ] ( expression | -f <file> )
2019-08-09 17:58:11 +00:00
```
2019-08-14 11:38:39 +00:00
Type `bb --help` to see a full explanation of the options.
The `clojure.core` functions are accessible without a namespace alias.
The following Clojure namespaces are required by default and only available
through the aliases:
- `clojure.string` aliased as `str`
- `clojure.set` aliased as `set`
2019-08-14 16:18:23 +00:00
- `clojure.edn` aliased as `edn` (only `read-string` is available)
2019-08-14 11:38:39 +00:00
- `clojure.java.shell` aliases as `shell` (only `sh` is available)
From Java the following is available:
2019-08-13 22:19:15 +00:00
2019-08-17 20:19:46 +00:00
- `System`: `exit`, `getProperty`, `setProperty`, `getProperties`, `getenv`
2019-08-15 04:28:00 +00:00
Special vars:
2019-08-16 20:22:58 +00:00
- `*in*`: contains the input read from stdin (EDN by default, multiple lines with the `-i` option)
<!-- - `bb/*in*`: the unprocessed input from stdin -->
2019-08-15 04:28:00 +00:00
- `*command-line-args*`: contain the command line args
2019-08-09 12:51:42 +00:00
Examples:
``` shellsession
$ ls | bb -i '*in*'
2019-08-09 15:38:26 +00:00
["LICENSE" "README.md" "bb" "doc" "pom.xml" "project.clj" "reflection.json" "resources" "script" "src" "target" "test"]
$ ls | bb -i '(count *in*)'
12
2019-08-09 15:38:26 +00:00
$ bb '(vec (dedupe *in*))' <<< '[1 1 1 1 2]'
2019-08-09 12:51:42 +00:00
[1 2]
$ bb '(filterv :foo *in*)' <<< '[{:foo 1} {:bar 2}]'
[{:foo 1}]
2019-08-09 12:51:42 +00:00
```
2019-08-09 15:38:26 +00:00
``` shellsession
$ bb '(#(+ %1 %2 %3) 1 2 *in*)' <<< 3
2019-08-09 15:41:33 +00:00
6
```
``` shellsession
$ ls | bb -i '(filterv #(re-find #"reflection" %) *in*)'
2019-08-09 15:38:26 +00:00
["reflection.json"]
```
2019-08-13 22:19:15 +00:00
``` shellsession
2019-08-14 11:38:39 +00:00
$ bb '(run! #(shell/sh "touch" (str "/tmp/test/" %)) (range 100))'
2019-08-13 22:19:15 +00:00
$ ls /tmp/test | bb -i '*in*'
["0" "1" "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" "2" "20" "21" ...]
```
2019-08-10 20:52:30 +00:00
More examples can be found in the [gallery](#gallery).
2019-08-09 21:08:49 +00:00
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
```
Using `bb` with a shebang also works:
2019-08-15 07:04:14 +00:00
``` clojure
2019-08-15 07:33:11 +00:00
#!/usr/bin/env bb -f
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-08-17 15:38:24 +00:00
## Enabling SSL
2019-08-17 20:19:46 +00:00
If you want to be able to use SSL to e.g. run `(slurp
"https://www.clojure.org")` you will need to add the location where
`libsunec.so` or `libsunec.dylib` is located to the `java.library.path` Java
property. This library comes with most JVM installations, so you might already
have it on your machine. It is usually located in `<JAVA_HOME>/jre/lib` or
`<JAVA_HOME>/jre/<platform>/lib`. It is also bundled with GraalVM.
2019-08-17 15:38:24 +00:00
2019-08-17 20:19:46 +00:00
Example:
2019-08-17 15:38:24 +00:00
``` shellsession
2019-08-17 20:19:46 +00:00
$ cat /tmp/https_get.clj
#!/usr/bin/env bb -f
2019-08-17 15:38:24 +00:00
2019-08-17 20:19:46 +00:00
(System/setProperty
"java.library.path"
"/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib")
2019-08-17 15:38:24 +00:00
2019-08-17 20:19:46 +00:00
(slurp (first *command-line-args*))
2019-08-17 15:38:24 +00:00
```
2019-08-17 20:19:46 +00:00
``` shellsession
$ /tmp/https_get.clj https://www.google.com | bb '(subs *in* 0 50)'
"<!doctype html><html itemscope=\"\" itemtype=\"http:/"
```
2019-08-17 15:38:24 +00:00
2019-08-09 12:51:42 +00:00
## Test
2019-08-09 18:01:12 +00:00
Test on the JVM:
2019-08-09 12:51:42 +00:00
script/test
2019-08-09 18:01:12 +00:00
Although this tool doesn't offer any benefit when running on the JVM, it is
convenient for development.
2019-08-09 12:51:42 +00:00
Test the native version:
BABASHKA_TEST_ENV=native script/test
## Build
You will need leiningen and GraalVM.
2019-08-17 20:27:13 +00:00
This repo contains a submodule, so you will have clone that too. If you're
doing that for the first time:
``` shellsession
$ git submodule update --init --recursive
```
and for subsequent updates:
``` shellsession
$ git submodule update --recursive
```
To build this project, set `$GRAALVM_HOME` to the GraalVM distribution directory.
Then run:
2019-08-09 12:51:42 +00:00
script/compile
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)
## Gallery
Here's a gallery of more useful examples. Do you have a useful example? PR
welcome!
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
$ < /tmp/test.txt bb -io '(shuffle *in*)'
2019-08-13 11:43:15 +00:00
3 Babashka
2019-08-10 20:52:30 +00:00
2 Clojure
4 Goodbye
1 Hello
```
### Fetch latest Github release tag
For converting JSON to EDN, see [jet](https://github.com/borkdude/jet).
``` shellsession
2019-08-11 07:05:25 +00:00
$ curl -s https://api.github.com/repos/borkdude/babashka/tags |
2019-08-12 07:28:37 +00:00
jet --from json --keywordize --to edn |
bb '(-> *in* first :name (subs 1))'
2019-08-10 18:34:24 +00:00
"0.0.4"
```
2019-08-10 18:52:13 +00:00
### Get latest OS-specific download url from Github
2019-08-10 18:34:24 +00:00
``` shellsession
2019-08-11 07:05:25 +00:00
$ curl -s https://api.github.com/repos/borkdude/babashka/releases |
2019-08-12 07:28:37 +00:00
jet --from json --keywordize |
bb '(-> *in* first :assets)' |
bb '(some #(re-find #".*linux.*" (:browser_download_url %)) *in*)'
2019-08-10 18:52:13 +00:00
"https://github.com/borkdude/babashka/releases/download/v0.0.4/babashka-0.0.4-linux-amd64.zip"
```
2019-08-15 09:43:59 +00:00
## Support this project
Do you enjoy this project? Consider buying me a [hot
beverage](https://ko-fi.com/borkdude).
2019-08-09 12:51:42 +00:00
## License
Copyright © 2019 Michiel Borkent
Distributed under the EPL License, same as Clojure. See LICENSE.