dundalek / closh

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

POSIX Shell compability #3

Closed xzfc closed 6 years ago

xzfc commented 7 years ago

Have you considered using POSIX Shell compatible syntax?

That would allow sourcing shell scrips (e.g. virtualenv activate).

As far I can see, POSIX Shell have following incompatibles with closh:


One way of having compatible syntax is make sure that that every part of syntax of closh is either have same meaning in POSIX Shell or invalid in POSIX Shell. For example, prepend every clojure command with %, and use |} instead of |>:

ls | %#(clojure.string/replace % #"\.txt" ".md")
ls |} %(reverse) | %(take 5)

Another one is using shopt to enable/disable POSIX compatible mode:

shopt no_closh # only POSIX syntax allowed from now on
source ./bin/activate
ls_root() ( cd / && echo * | tr ' ' '\n' )
shopt closh # closh syntax is enabled back
ls_root |> (reverse)
dundalek commented 7 years ago

I am leaning towards not having fully POSIX compatible syntax. What is the outcome you need from sourcing the existing scripts? Is that mainly to set up some environment variables?

xzfc commented 7 years ago

What is the outcome you need from sourcing the existing scripts? Is that mainly to set up some environment variables?

Yes, setting environment variables. These scripts can be complex and even taking arguments but the main outcome is setting variables. Example: iccvars.sh, needed for intel c++ compiler doc src:compilervars.sh src:compilervars_arch.sh.

It can be done using something like bash -c "source posix-shell-file.sh; exec closh" (instead of just source posix-shell-file.sh), but that will spawn new shell, not update state of the running one (i.e. losing all defns, etc).

mnewt commented 7 years ago

This is doable using a wrapper like fish shell's bass.

Wrappers like bass work by executing the script using its native interpreter (in this case bash), taking a diff of the before and after state, and replaying that diff into the environment.

I think this is the best way to do it because what is the point of replicating POSIX in a project like this? closh exists because POSIX isn't what we want. Best to capture the environment variables from sourcing a bash/POSIX/whatever script and stick them in closh.

I actually implemented my own in fish (fix). It has its limitations but it made me familiar with the problem domain and why I don't want to code something like that in a POSIX-inspired shell again (even fish).

If y'all agree with that much, the only thing left would be how to integrate the functionality into closh. It could be a native operator, a function, or just an external utility. My vote is for a function, because this is going to be a common sticking point for those who want to use virtualenv or whatever. If it's included in closh itself, it lowers the friction considerably.

Let me know what you think and I can hack something together.

dundalek commented 7 years ago

@mnewt Thanks, you are spot on with the reason for not replicating POSIX. I should probably start writing down some design philosophy document and add it there. I think the bass / fix approach is great for solving this particular need. If you are able to hack something up that would be really cool.

@xzfc Do you think this solution would solve your need or can you think of additional cases?

xzfc commented 7 years ago

I don't have additional use-cases. Wrapper approach sounds good to me.

mnewt commented 7 years ago

@dundalek Yes, I'll create a helper function in core.cljs, unless you think a different approach is better.

dundalek commented 7 years ago

@mnewt Cool! I think we might want to create util.cljs or something like that and have it there.

mnewt commented 7 years ago

@dundalek I created a proof of concept "source" helper function. Probably needs a different name, some tweaking, whatever. I will create tests and docs before making a PR.

https://github.com/mnewt/closh/commit/2800196fcc1a422c6eece99023bb0a2beca149c3

However, I can't seem to plug it in to eval.cljs properly. This is what I get when I try to invoke it:

$ (source "export P=1")
Cannot read property 'source' of undefined
     (evalmachine.<anonymous>:1:11)
     ContextifyScript.Script.runInThisContext (vm.cljs:44:33)
     Object.runInThisContext (vm.cljs:116:38)
     (Object.lt)
     (Object.lumo.repl.caching_node_eval)
     (NO_SOURCE_FILE <embedded>:5720:273)
     z (NO_SOURCE_FILE <embedded>:5721:263)
     Object.cljs.js.eval_str_STAR_ (NO_SOURCE_FILE <embedded>:5722:328)
     Function.cljs.js.eval_str.cljs$core$IFn$_invoke$arity$5 (NO_SOURCE_FILE <embedded>:5725:508)
     lumo.repl.execute_text (NO_SOURCE_FILE <embedded>:6492:316)

"$ "

This works fine in lumo (https://gist.github.com/mnewt/c51a157b7f33ae768d31d8bffec8f1c0)

Can you point me toward the fix?

dundalek commented 7 years ago

@mnewt Cool! If you add [closh.util] into require statements in main.cljs it will work (and keep it in eval as is). I agree on a different name since we don't want to override the Clojure's source function.

mnewt commented 6 years ago

This should be fixed with #35

dundalek commented 6 years ago

Yeah, cool!