This commit is contained in:
Michiel Borkent 2019-08-14 00:19:15 +02:00 committed by GitHub
parent aa8a66c230
commit a0cd0b58d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 138 additions and 40 deletions

View file

@ -17,7 +17,7 @@ $ bb '(vec (dedupe *in*))' <<< '[1 1 1 1 2]'
## Rationale
If you're a bash expert, you probably don't need this. But for those of us who
can use a bit of Clojure in their shell scripts, it may be useful.
scan use a bit of Clojure in their shell scripts, it may be useful.
Properties:
@ -66,7 +66,7 @@ You may also download a binary from [Github](https://github.com/borkdude/babashk
## Usage
``` shellsession
... | bb [-i] [-o] '<Clojure form>'
bb [ --help ] [ -i ] [ -o ] [ -io ] [ --version ] [ expression ]
```
There is one special variable, `*in*`, which is the input read from stdin. The
@ -77,8 +77,14 @@ shell-scripting friendly output. To combine `-i` and `-o` you can use `-io`.
The current version can be printed with `bb --version`.
Currently only the macros `if`, `when`, `and`, `or`, `->`, `->>` and `as->` are
supported.
Currently only the following special forms/macros are supported: anonymous
function literals like `#(%1 %2)`, `quote`, `do`,`if`, `when`, `let`, `and`,
`or`, `->`, `->>`, `as->`.
The `clojure.core` functions are accessible without a namespace alias. Those in
`clojure.string` are accessed through the alias `str`, like:
`str/includes?`. Those in `clojure.set` using the alias `set`, like:
`set/difference`.
Examples:
@ -109,6 +115,15 @@ $ ls | bb -i '(filterv #(re-find #"reflection" %) *in*)'
["reflection.json"]
```
Shell commands can be executed using `csh` which is an alias for
`clojure.java.shell/sh`:
``` shellsession
$ bb '(run! #(csh "touch" (str "/tmp/test/" %)) (range 100))'
$ ls /tmp/test | bb -i '*in*'
["0" "1" "10" "11" "12" "13" "14" "15" "16" "17" "18" "19" "2" "20" "21" ...]
```
More examples can be found in the [gallery](#gallery).
## Test

View file

@ -9,7 +9,7 @@
:url "http://opensource.org/licenses/eclipse-1.0.php"}
:source-paths ["src"]
:dependencies [[org.clojure/clojure "1.9.0"]
[borkdude/sci "0.0.2"]]
[borkdude/sci "0.0.4"]]
:profiles {:clojure-1.9.0 {:dependencies [[org.clojure/clojure "1.9.0"]]}
:clojure-1.10.1 {:dependencies [[org.clojure/clojure "1.10.1"]]}
:test {:dependencies [[clj-commons/conch "0.9.2"]]}

View file

@ -1,9 +1,33 @@
[
{
"name": "java.lang.Class",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true
}
{
"name": "java.lang.Class",
"allDeclaredConstructors": true,
"allPublicConstructors": true,
"allDeclaredMethods": true,
"allPublicMethods": true
},
{
"name":"java.io.BufferedReader",
"allPublicMethods":true
},
{
"name":"java.lang.Process",
"allPublicMethods":true
},
{
"name":"java.lang.ProcessBuilder",
"allPublicConstructors":true
},
{
"name":"java.lang.String",
"allPublicMethods":true
},
{
"name":"java.lang.UNIXProcess",
"allPublicMethods":true
},
{
"name":"java.util.concurrent.LinkedBlockingQueue",
"allPublicMethods":true
}
]

View file

@ -1 +1 @@
0.0.7-SNAPSHOT
0.0.7

View file

@ -17,8 +17,10 @@ $GRAALVM_HOME/bin/native-image \
-J-Dclojure.spec.skip-macros=true \
-J-Dclojure.compiler.direct-linking=true \
"-H:IncludeResources=BABASHKA_VERSION" \
"-H:IncludeResources=SCI_VERSION" \
-H:ReflectionConfigurationFiles=reflection.json \
--initialize-at-build-time \
--initialize-at-run-time=java.lang.Math\$RandomNumberGeneratorHolder \
--initialize-at-build-time \
-H:Log=registerResource: \
--verbose \
--no-fallback \

View file

@ -2,7 +2,7 @@
set -eo pipefail
if [ "$JET_TEST_ENV" = "native" ]; then
if [ "$BABASHKA_TEST_ENV" = "native" ]; then
lein test
else
echo "Testing with Clojure 1.9.0"

View file

@ -3,11 +3,15 @@
(:require
[clojure.edn :as edn]
[clojure.java.io :as io]
[clojure.java.shell :as cjs]
[clojure.string :as str :refer [starts-with?]]
[sci.core :as sci])
(:gen-class))
(set! *warn-on-reflection* true)
;; To detect problems when generating the image, run:
;; echo '1' | java -agentlib:native-image-agent=config-output-dir=/tmp -jar target/babashka-xxx-standalone.jar '...'
;; with the java provided by GraalVM.
(defn read-edn [s]
(edn/read-string
@ -33,35 +37,83 @@
(get opts "-io")))
raw-out (boolean (or (get opts "-o")
(get opts "-io")))
println? (boolean (get opts "--println"))]
println? (boolean (get opts "--println"))
help? (boolean (get opts "--help"))]
{:version version
:raw-in raw-in
:raw-out raw-out
:println? println?}))
:println? println?
:help? help?}))
(defn parse-shell-string [s]
(str/split s #"\n"))
(defn print-version []
(println (str "babashka v"(str/trim (slurp (io/resource "BABASHKA_VERSION"))))))
(def usage-string "Usage: [ --help ] [ -i ] [ -o ] [ -io ] [ --version ] [ expression ]")
(defn print-usage []
(println usage-string))
(defn print-help []
(println (str "babashka v" (str/trim (slurp (io/resource "BABASHKA_VERSION")))))
(println (str "sci v" (str/trim (slurp (io/resource "SCI_VERSION")))))
(println)
(print-usage)
(println)
(println "Options:")
(println "
--help: print this help text.
--version: print the current version of babashka.
-i: read shell input into a list of strings instead of reading EDN.
-o: write shell output instead of EDN.
-io: combination of -i and -o.
"))
(defn main
[& args]
(or
(let [{:keys [:version :raw-in :raw-out :println?
:help?]} (parse-opts args)]
(second
(cond version
[(print-version) 0]
help?
[(print-help) 0]
:else
(try
[(let [exprs (drop-while #(str/starts-with? % "-") args)
_ (when (not= (count exprs) 1)
(throw (Exception. ^String usage-string)))
expr (last args)
in (delay (let [in (slurp *in*)]
(if raw-in
(parse-shell-string in)
(read-edn in))))
res (sci/eval-string
expr
{:bindings {(with-meta '*in*
{:sci/deref! true}) in
'run! run!
'csh cjs/sh}})]
(if raw-out
(if (coll? res)
(doseq [l res]
(println l))
(println res))
((if println? println? prn) res))) 0]
(catch Exception e
(binding [*out* *err*]
(println (str/trim
(or (:stderr (ex-data e))
(.getMessage e))) ))
[nil 1])))))
1))
(defn -main
[& args]
(let [{:keys [:version :raw-in :raw-out :println?]} (parse-opts args)]
(cond version
(println (str/trim (slurp (io/resource "BABASHKA_VERSION"))))
:else
(let [expr (last args)
in (slurp *in*)
in (if raw-in
(str/split in #"\n")
(read-edn in))
;; _ (prn in)
res (try (sci/eval-string expr {:bindings {'*in* in}})
(catch Exception e
(binding [*out* *err*]
(println (.getMessage e)))
(System/exit 1)))]
(if raw-out
(if (coll? res)
(doseq [l res]
(println l))
(println res))
((if println? println? prn) res))))))
(System/exit (apply main args)))
;;;; Scratch

View file

@ -2,6 +2,7 @@
(:require
[clojure.test :as test :refer [deftest is testing]]
[babashka.test-utils :as test-utils]
[babashka.main :as main]
[clojure.edn :as edn]
[clojure.string :as str]))
@ -50,7 +51,7 @@
"-io"
(str '(shuffle *in*)))
out-lines (set (str/split out #"\n"))]
(= in-lines out-lines)))
(is (= in-lines out-lines))))
(testing "find occurrences in file by line number"
(is (= '(1 3)
(->
@ -58,3 +59,7 @@
"-i"
"(map-indexed #(-> [%1 %2]) *in*)")
(bb "(keep #(when (re-find #\"(?i)clojure\" (second %)) (first %)) *in*)"))))))
(deftest input-test
(testing "bb doesn't wait for input if *in* isn't used"
(is (= "2\n" (with-out-str (main/main "(inc 1)"))))))

View file

@ -8,7 +8,7 @@
(defn bb-jvm [input & args]
(with-out-str
(with-in-str input
(apply main/-main args))))
(apply main/main args))))
(defn bb-native [input & args]
(let-programs [bb "./bb"]