Babashkaで遊ぼう

日常的にスクリプトでもClojureを使いたいので、Babashkaにキャッチアップする試み。

Waht is Babashka?
Babashkaとは、通常 bash などを使用して実行することを Clojure で簡単に行うことができる、スクリプト実行環境、と言えると思います。使用例は、ビルドスクリプト、コマンドラインユーティリティ、小規模なWebアプリケーション、git hook、AWS Lambda functions などだそうです。リソースが少ない時や起動が高速であるべき時などに向いているようです。
Babashkaは、standalone natively-compiled binary です。つまり、OSはJVMを実行するのではなく、Babashkaバイナリを実行します。コンパイルされたBabashkaバイナリは多くのClojureの名前空間とライブラリを持っています。(Babashka book Built-in namespaces)
Clojureを知っている人にとって、一言で言ってしまうと『起動が早いClojureでシェルスクリプトが作れる』ってことでいいのではないでしょうか。
つまり、JVMのオーバーヘッドを気にすることなく、スクリプトでClojureが使えるって感じ。
公式サイト
ソース
記事など
記事

インストール
前になにかの折に入れてたので省略!
一応疎通確認。
$ bb --version
babashka v1.12.194

Hello world
とりあえず動作確認で動かしてみる。
まずファイルを作成する。
touch hello.clj
これをエディターで開いて、以下のようにコマンドを記載する。
(println "Hello Babashka!")
あとはターミナルから実行すればOK
$ bb helo.clj
Hello Babashka!

スクリプトを実行可能にする
#!/usr/bin/env bb
(println "Hello Babashka!")
実行権限を追加する。
chmod +x hello.clj
あとはシェルスクリプトファイルの様に実行すればいい。
$ ./helo.clj
Hello Babashka!

ユーザの入力を受け付ける
とりあえず、スクリプトで基本の一つである、ユーザ入力受付をやってみよう。
(println "入力してください")
(def input (read-line))
(println "入力された文字列はこれです:" input)
$ bb a.clj
入力してください
123
入力された文字列はこれです: 123

引数受付
引数の入力を表示する場合は以下のようになる。
(def args *command-line-args*)
(println "引数:" args)
$ bb a.clj "123" "2" "3"
引数: (123 2 3)

四則演算
とりあえず簡単に四則演算を行う。
(println "最初の数を入力してください")
(def a (Integer/parseInt (read-line)))
(println "次の数を入力してください")
(def b (Integer/parseInt (read-line)))
(println "足し算 " (+ a b))
(println "引き算 " (- a b))
(println "掛け算 " (* a b))
(println "割り算 " (/ a b))
bb a.clj
最初の数を入力してください
10
次の数を入力してください
5
足し算 15
引き算 5
掛け算 50
割り算 2

交通費計算プログラム
月の交通費を計算するスクリプトを書いてみよう。
(def basic-expense 300)
(def business-trip-expense 514)
(println "今月の出勤の日数を入力してください。")
(def workdays (Integer/parseInt (read-line)))
(println "出張の日数を入力してください。")
(def business-trip-days (Integer/parseInt (read-line)))
(defn calculate-transportation-expenses [days trip-days]
(let [transportation-cost (* (* 2 basic-expense) days)
work-trip-expenxes (* business-trip-expense trip-days)]
(+ transportation-cost work-trip-expenxes)))
(println "今月の交通費の合計は以下になります。")
(println (calculate-transportation-expenses workdays business-trip-days))
ユーザの入力はread-line
で取得できる。
String型として取得されるようなので、Integer/parseInt
を用いてInteger型に変換する必要がある。
以下はread-lineの型情報参考。
test.clj
(println (type (read-line)))
$ bb test.clj
3 4
java.lang.String
実際に動作させるところ。
$ bb calc.clj
今月の出勤の日数を入力してください。
17
出張の日数を入力してください。
3
今月の交通費の合計は以下になります。
12108

ファイル操作
ファイルの基本的にな操作。
java.ioを使うようだ。
(require '[clojure.java.io :as io])
;; ファイルに書き出す。
(spit "example.txt" "Hello, Babashka!")
;; ファイルの内容を読み込む。
(let [content (slurp "example.txt")]
(println "ファイルの内容:" content))
;; ディレクトリ内のファイルを一覧。
(doseq [file (.listFiles (java.io.File. "."))]
(println (.getName file)))

簡易的なcurlツール
引数でもらったURLでGETリクエストするだけの単純なツール。
(require '[babashka.curl :as curl])
(def fetch-pass (first *command-line-args*))
(let [response (curl/get fetch-pass {:as :json})]
(println "Status:" (:status response))
(println "Data:" (:body response)))
使い方は以下のようになる。
$ bb curl.clj "https://jsonplaceholder.typicode.com/todos/1"
Status: 200
Data: {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
ダブルクォーテーションで囲わなくてもいける。
bb curl.clj https://jsonplaceholder.typicode.com/todos/1
Status: 200
Data: {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}

削除前提のメモ
- メール送信プログラムを書いてみる。