dundalek / closh

Bash-like shell based on Clojure
Eclipse Public License 1.0
1.62k stars 66 forks source link

Option to pass script to closh #2

Closed borkdude closed 5 years ago

borkdude commented 6 years ago

Is it possible to pass a script to closh, so you can execute a script from, say, bash?

bash$ closh myscript.closh

or

#!/usr/local/bin/closh
ls |> #(println (coun t%))
dundalek commented 6 years ago

Not yet, I think I would like to purposefully delay support for that. Closh is still very early in the development, syntax may change a bit. Which is bearable for interactive use but I don't want to break people's scripts.

It is actually quite nice to write scripts on top of lumo itself with shelljs used for more convenience. Maybe I can write a guide with some examples how to do that.

borkdude commented 6 years ago

As long as the changes are listed somewhere (e.g. in a CHANGES.md), so I know what to upgrade, I wouldn't mind having breaking scripts. Example of lumo + shelljs is more than welcome!

jcburley commented 6 years ago

I think that second example should read:

#!/usr/local/bin/closh
ls |> #(println (count %))

(I.e. transpose the space and "t" before "%".)

jcburley commented 6 years ago

FWIW, I'm toying with the idea of re-implementing portions of my email-server (MTA) setup, currently a hacked version of qmail, in some variant of Clojure, and was looking to closh for fast script startup times (having investigated ClojureC earlier this year and been pointed to ClojureScript on v8 generally by its author in a nice chat we had).

It's still unclear to me whether I want to write scripts in closh, lumo, ClojureScript, or even Clojure (since the startup time I'm measuring for java -jar clojure.jar < /dev/null seems close to that of closh < /dev/null and not much worse than lumo < /dev/null -- though still some 400x slower than bash < /dev/null).

Anyway, I was thinking that perhaps experimental support for this could be provided by requiring a --VBETA=n argument that must match the version of the shell language and features implemented therein, in order to read the input file (and/or the rest of the command-line arguments)?

That way, only those of us willing to take risks here would be able to get something like this to work:

#!/usr/bin/closh --VBETA=0
ls |> (count)

(And, yes, I'm willing to help out here...or elsewhere with Clojure, once I find my feet and some time.)

dundalek commented 6 years ago

Hi, if startup time is priority and you don't mind the compilation step then I can think of two options:

1) Write it in ClojureScript, then compile to JS and run it with vanilla node.js (which uses the V8 engine). That should be like an order of magnitute faster than lumo. 2) Write it in Clojure and then try to compile it into native executable with Graal.

An interesting option might be to utilize 1) or 2) and use closh as a library to make it easier to shell out to existing commands. When you type commands in closh it internally wraps them in sh macro which does the transformation.

Closh's relationship to lumo is that closh is just a program that runs using lumo. I am currently focused on the interactive mode and haven't spend any time making the script mode work. Therefore if the compilation step is not acceptable then it might be better just to run lumo directly and use closh as a library:

#!/usr/bin/env lumo

; require closh macros here

(sh ls |> (count))

I am in currently in the process of porting closh to also run on JVM, so very soon it will also be possible to run it with Clojure proper. Here is running the code from dev branch:

#!/bin/sh

clojure -Sdeps '{:deps {closh {:git/url "https://github.com/dundalek/closh.git" :sha "f6416f81bf132a037fcf437ed07dc09adb4c14b7"}}}' - <<END

(require '[closh.zero.macros :refer :all]
         '[closh.zero.core :refer :all]
         '[closh.zero.pipeline :refer :all])

(println "hello clojure")

(sh echo hello closh)

(println "Number of files:" (sh-val ls |> (count)))

END

Please note were at the frontier here pushing boundaries so expect some rough edges and unexpected bugs. I am happy for any help and bug reports.

jeroenvandijk commented 6 years ago

Another idea to support scripting from a closh shell (to have fast boot time) is to have a command say closh-script. This command would be built from another command eval-clj [1]:

Unfortunately, evaluation in cljs is asynchronous and namespaces need to be required upfront I believe (see output [2]). I'm guessing though that this approach could work quite well for the Java version, as this tackles both issues. I wasn't able to test this with the dependency mentioned here though [3]

[1]

(defcmd eval-clj [script] (cljs.js/eval-str (cljs.js/empty-state) script "dontcare" {:eval cljs.js/js-eval} cljs.core/identity))

[2]

$ echo "(cljs.core/range 10)" | (eval-clj)
     WARNING: Use of undeclared Var cljs.user/sh

                  ⬆
     WARNING: Use of undeclared Var cljs.core/range at line 1

[3] Output of clj script

org.eclipse.jgit.api.errors.JGitInternalException: Missing unknown f6416f81bf132a037fcf437ed07dc09adb4c14b7
dundalek commented 5 years ago

I've written initial design proposal for the script mode here: https://www.reddit.com/r/closh/comments/a71usj/script_mode_design_proposal_for_closh/

Any feedback appreciated.

dundalek commented 5 years ago

Initial iteration of script mode has now landed in the latest release v0.4, you can read how to use it in the scripting docs. I will be glad for any feedback, experience or issue reports.