[#14] implement --stream option
This commit is contained in:
parent
2de8cf8361
commit
d1ea9f8360
4 changed files with 63 additions and 33 deletions
|
|
@ -75,7 +75,7 @@ You may also download a binary from [Github](https://github.com/borkdude/babashk
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
``` shellsession
|
``` shellsession
|
||||||
bb [ --help ] | [ --version ] | ( [ -i ] [ -o ] | [ -io ] ) ( expression | -f <file> )
|
bb [ --help ] | [ --version ] | ( [ -i ] [ -o ] | [ -io ] ) [ --stream ] ( expression | -f <file> )
|
||||||
```
|
```
|
||||||
|
|
||||||
Type `bb --help` to see a full explanation of the options.
|
Type `bb --help` to see a full explanation of the options.
|
||||||
|
|
@ -84,7 +84,8 @@ The input is read as EDN by default. If the `-i` flag is provided, then the
|
||||||
input is read as a string which is then split on newlines. The output is printed
|
input is read as a string which is then split on newlines. The output is printed
|
||||||
as EDN by default, unless the `-o` flag is provided, then the output is turned
|
as EDN by default, unless the `-o` flag is provided, then the output is turned
|
||||||
into shell-scripting friendly output. To combine `-i` and `-o` you can use
|
into shell-scripting friendly output. To combine `-i` and `-o` you can use
|
||||||
`-io`.
|
`-io`. When using the `--stream` option the expression is executed for every
|
||||||
|
line or EDN value from stdin.
|
||||||
|
|
||||||
The `clojure.core` functions are accessible without a namespace alias.
|
The `clojure.core` functions are accessible without a namespace alias.
|
||||||
|
|
||||||
|
|
@ -102,7 +103,8 @@ From Java the following is available:
|
||||||
|
|
||||||
Special vars:
|
Special vars:
|
||||||
|
|
||||||
- `*in*`: contains the input read from stdin
|
- `*in*`: contains the input read from stdin (EDN by default, multiple lines with the `-i` option)
|
||||||
|
<!-- - `bb/*in*`: the unprocessed input from stdin -->
|
||||||
- `*command-line-args*`: contain the command line args
|
- `*command-line-args*`: contain the command line args
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ fi
|
||||||
|
|
||||||
BABASHKA_VERSION=$(cat resources/BABASHKA_VERSION)
|
BABASHKA_VERSION=$(cat resources/BABASHKA_VERSION)
|
||||||
|
|
||||||
|
# We also need to AOT sci, else something didn't work in the Mac build on CircleCI
|
||||||
|
# See https://github.com/oracle/graal/issues/1613
|
||||||
( cd /tmp; git clone https://github.com/borkdude/sci 2> /dev/null || true )
|
( cd /tmp; git clone https://github.com/borkdude/sci 2> /dev/null || true )
|
||||||
mkdir -p src/sci
|
mkdir -p src/sci
|
||||||
cp -R /tmp/sci/src/* src
|
cp -R /tmp/sci/src/* src
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,6 @@
|
||||||
;; echo '1' | java -agentlib:native-image-agent=config-output-dir=/tmp -jar target/babashka-xxx-standalone.jar '...'
|
;; echo '1' | java -agentlib:native-image-agent=config-output-dir=/tmp -jar target/babashka-xxx-standalone.jar '...'
|
||||||
;; with the java provided by GraalVM.
|
;; with the java provided by GraalVM.
|
||||||
|
|
||||||
(defn read-edn [s]
|
|
||||||
(edn/read-string
|
|
||||||
{:readers *data-readers*}
|
|
||||||
s))
|
|
||||||
|
|
||||||
(defn- parse-opts [options]
|
(defn- parse-opts [options]
|
||||||
(let [opts (loop [options options
|
(let [opts (loop [options options
|
||||||
opts-map {}]
|
opts-map {}]
|
||||||
|
|
@ -25,6 +20,9 @@
|
||||||
(case opt
|
(case opt
|
||||||
("--version") {:version true}
|
("--version") {:version true}
|
||||||
("--help") {:help? true}
|
("--help") {:help? true}
|
||||||
|
("--stream") (recur (rest options)
|
||||||
|
(assoc opts-map
|
||||||
|
:stream? true))
|
||||||
("-i") (recur (rest options)
|
("-i") (recur (rest options)
|
||||||
(assoc opts-map
|
(assoc opts-map
|
||||||
:raw-in true))
|
:raw-in true))
|
||||||
|
|
@ -55,7 +53,7 @@
|
||||||
(defn print-version []
|
(defn print-version []
|
||||||
(println (str "babashka v"(str/trim (slurp (io/resource "BABASHKA_VERSION"))))))
|
(println (str "babashka v"(str/trim (slurp (io/resource "BABASHKA_VERSION"))))))
|
||||||
|
|
||||||
(def usage-string "Usage: bb [ --help ] | [ --version ] | ( [ -i ] [ -o ] | [ -io ] ) ( expression | -f <file> )")
|
(def usage-string "Usage: bb [ --help ] | [ --version ] | ( [ -i ] [ -o ] | [ -io ] ) [ --stream ] ( expression | -f <file> )")
|
||||||
(defn print-usage []
|
(defn print-usage []
|
||||||
(println usage-string))
|
(println usage-string))
|
||||||
|
|
||||||
|
|
@ -73,7 +71,8 @@
|
||||||
-i: read shell input into a list of strings instead of reading EDN.
|
-i: read shell input into a list of strings instead of reading EDN.
|
||||||
-o: write shell output instead of EDN.
|
-o: write shell output instead of EDN.
|
||||||
-io: combination of -i and -o.
|
-io: combination of -i and -o.
|
||||||
--file or -f: read expression from file instead of argument
|
--stream: stream over lines or EDN values from stdin. Combined with -i *in* becomes a single line per iteration.
|
||||||
|
--file or -f: read expressions from file instead of argument wrapped in an implicit do.
|
||||||
"))
|
"))
|
||||||
|
|
||||||
(defn read-file [file]
|
(defn read-file [file]
|
||||||
|
|
@ -118,6 +117,10 @@
|
||||||
'System/getProperties get-properties
|
'System/getProperties get-properties
|
||||||
'System/exit exit})
|
'System/exit exit})
|
||||||
|
|
||||||
|
(defn read-edn []
|
||||||
|
(edn/read {;;:readers *data-readers*
|
||||||
|
:eof ::EOF} *in*))
|
||||||
|
|
||||||
(defn main
|
(defn main
|
||||||
[& args]
|
[& args]
|
||||||
#_(binding [*out* *err*]
|
#_(binding [*out* *err*]
|
||||||
|
|
@ -125,7 +128,7 @@
|
||||||
(or
|
(or
|
||||||
(let [{:keys [:version :raw-in :raw-out :println?
|
(let [{:keys [:version :raw-in :raw-out :println?
|
||||||
:help? :file :command-line-args
|
:help? :file :command-line-args
|
||||||
:expression] :as _opts} (parse-opts args)]
|
:expression :stream?] :as _opts} (parse-opts args)]
|
||||||
#_(binding [*out* *err*]
|
#_(binding [*out* *err*]
|
||||||
(prn ">>" _opts))
|
(prn ">>" _opts))
|
||||||
(second
|
(second
|
||||||
|
|
@ -135,25 +138,36 @@
|
||||||
[(print-help) 0]
|
[(print-help) 0]
|
||||||
:else
|
:else
|
||||||
(try
|
(try
|
||||||
[(do (when-not (or expression file)
|
|
||||||
(throw (Exception. "Missing expression.")))
|
|
||||||
(let [expr (if file (read-file file) expression)
|
(let [expr (if file (read-file file) expression)
|
||||||
in (delay (let [in (slurp *in*)]
|
read-next #(if stream?
|
||||||
|
(if raw-in (or (read-line) ::EOF)
|
||||||
|
(read-edn))
|
||||||
|
(delay (let [in (slurp *in*)]
|
||||||
(if raw-in
|
(if raw-in
|
||||||
(parse-shell-string in)
|
(parse-shell-string in)
|
||||||
(read-edn in))))
|
(edn/read-string in)))))]
|
||||||
res (sci/eval-string
|
(loop [in (read-next)]
|
||||||
|
(if (identical? ::EOF in)
|
||||||
|
[nil 0] ;; done streaming
|
||||||
|
(let [res [(do (when-not (or expression file)
|
||||||
|
(throw (Exception. "Missing expression.")))
|
||||||
|
(let [res (sci/eval-string
|
||||||
expr
|
expr
|
||||||
{:bindings (assoc bindings
|
{:bindings (assoc bindings
|
||||||
(with-meta '*in*
|
(with-meta '*in*
|
||||||
{:sci/deref! true}) in
|
(when-not stream? {:sci/deref! true})) in
|
||||||
|
#_(with-meta 'bb/*in*
|
||||||
|
{:sci/deref! true}) #_do-in
|
||||||
'*command-line-args* command-line-args)})]
|
'*command-line-args* command-line-args)})]
|
||||||
(if raw-out
|
(if raw-out
|
||||||
(if (coll? res)
|
(if (coll? res)
|
||||||
(doseq [l res]
|
(doseq [l res]
|
||||||
(println l))
|
(println l))
|
||||||
(println res))
|
(println res))
|
||||||
((if println? println? prn) res)))) 0]
|
((if println? println? prn) res)))) 0]]
|
||||||
|
(if stream?
|
||||||
|
(recur (read-next))
|
||||||
|
res)))))
|
||||||
(catch Exception e
|
(catch Exception e
|
||||||
(binding [*out* *err*]
|
(binding [*out* *err*]
|
||||||
(when-let [msg (or (:stderr (ex-data e))
|
(when-let [msg (or (:stderr (ex-data e))
|
||||||
|
|
|
||||||
|
|
@ -70,8 +70,20 @@
|
||||||
(doseq [s res]
|
(doseq [s res]
|
||||||
(is (not-empty s)))))
|
(is (not-empty s)))))
|
||||||
|
|
||||||
(deftest malformed-command-line-args
|
(deftest malformed-command-line-args-test
|
||||||
(is (thrown-with-msg? Exception #"File does not exist: non-existing\n"
|
(is (thrown-with-msg? Exception #"File does not exist: non-existing\n"
|
||||||
(bb nil "-f" "non-existing")))
|
(bb nil "-f" "non-existing")))
|
||||||
(is (thrown-with-msg? Exception #"Missing expression.\n"
|
(is (thrown-with-msg? Exception #"Missing expression.\n"
|
||||||
(bb nil))))
|
(bb nil))))
|
||||||
|
|
||||||
|
#_(deftest raw-in-test
|
||||||
|
(is (= "[1 2 3\n4 5 6 [\"1 2 3\" \"4 5 6\"]]"
|
||||||
|
(bb "1 2 3\n4 5 6" "-i" "(format \"[%s %s]\" bb/*in* *in*)'"))))
|
||||||
|
|
||||||
|
(deftest stream-test
|
||||||
|
(is (= "2\n3\n4\n" (test-utils/bb "1 2 3" "--stream" "(inc *in*)")))
|
||||||
|
(is (= "2\n3\n4\n" (test-utils/bb "{:x 2} {:x 3} {:x 4}" "--stream" "(:x *in*)")))
|
||||||
|
(let [x "foo\n\bar\n"]
|
||||||
|
(is (= x (test-utils/bb x "--stream" "-io" "*in*"))))
|
||||||
|
(let [x "f\n\b\n"]
|
||||||
|
(is (= x (test-utils/bb x "--stream" "-io" "(subs *in* 0 1)")))))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue