Elvish, expressive programming language and a versatile interactive shell
elv.sh
elv.sh
The Elvish shell language doesn't change much which is nice nowadays. The Elvish executable has also got a built-in lsp which is another nice touch.
Finally it is a "proper" shell because it has job control (which other new age shells like nushell don't).
P.S. I like nushell too which has got more features, is more powerful but is still evolving. Maybe in a couple of more years it will stablize like Elvish already has. There are lots of similarities between Nushell and Elvish scripting language. I would recommend either of them.
Re job control - you can run a background job with &, and there are fg and bg commands, but you can't actually ^Z a running program (which is what most people mean by job control). I've heard people have success with https://github.com/yshui/job-security though.
Re Elvish and Nushell, I'd add my biased recommendation for Elvish because it has comprehensive reference documents (https://elv.sh/ref/) :)
An imperfect analogy: Elvish is like a compact OCaml while nushell is more like a Haskell or C++ of the scripting world !
See e.g. the top-right xterm in https://trout.me.uk/screenshot3.jpg for why.
Tried to switch to a ksh for a while and ran face first into that, though OpenBSD ksh has added it so I may attempt that one at some point.
(tabs don't fit my brain nearly as well and https://github.com/n-t-roff/heirloom-ex-vi is my usual editor and doesn't provide anything of that ilk anyway ... I may be an outlier ...)
The syntax and semantics seem really rather nice though, I may attempt elvish for scripting at some point.
Also very useful if you do a lot of context switching or are an occasional user, to make sure your brain and fingers are in "vim mode" (at least one other line in the file makes this rather obvious)
And once it's there, why bother removing it? I think you are in minority if you actively prune your vimrc for dead config...
I just tend to turn off things I only ever hit by accent (see also the :h override).
(my main reason for preferring original ex-vi is that when I typo a command, it beeps at me, whereas in vim the typo is usually some completely unrelated but valid command sequence and then WTF JUST HAPPENED and argh)
Being able to do e.g. %Task to resume the lib/.../Task.pm editor is -really- nice once you have that many editors open, though I try and stick to about 5 per xterm normally so %- %+ %<digit> would absolutely be enough to get started with.
There’s actually a lot of parallels between murex and Elvish. Both projects started around the same time, both written on Go. Both support structured data and smart data types.
The syntax differs a little between them two but concept wise they’re similar in a number of ways. I as (project author) just don’t do a particularly good job promoting murex. In part because I’d never want to force my own shell preferences onto others.
But It’s good that there’s so much choice these days and people aren’t stuck with the archaic decisions of the 70s
(Also just checked out Murex's website again, seems we reworked the style of the respective homepages around the same time :)
I can't take any credit for Murex's site, it was a user who doesn't write Go but wanted to contribute in other ways. Open source is definitely a double edged sword but I'm still always surprised and impressed with how much time people are willing to invest into other peoples projects if they like them.
By the way, one killer feature of Elvish that I admire and think doesn't get talked about enough is how exceptions are raised in pipes. eg:
~> cat example | grep foo | grep bar
Exception: (<nil> | grep exited with 1 | grep exited with 1)
It's really neat how you do that. It's readable, detailed, and valuable to the user.Kidding, I'll give it a try. Can it replace one's shell as daily driver?
There are detailed instructions on how to get it and use it as your default shell in https://elv.sh/get/.
Is there some humor I'm missing there, or is that just a typo (and you meant to say "I have" )?
In that case I think you should give Elvish a chance then :)
Honestly one of the motivations for writing a shell language was that I could never remember which of ${x%y} and ${x#y} is for trimming the suffix vs prefix. So Elvish just use function syntax - "str:trim-suffix $x y" and "str:trim-prefix $x y" - which I highlighted in the explainer of the first snippet (https://elv.sh/learn/scripting-case-studies.html#jpg-to-png....).
Bash will be dethroned some day. As general purpose languages like Rust, Julia, Python etc have risen, Fortran, pascal etc have declined. And so that will eventually happen with bash. Not “if” but “when” !
Re the cost of switching: on the one hand, I'd say that "switching" is really the wrong mentality, because you can always still use bash! You can "switch" to Elvish as your interactive shell and still use bash for scripting. Or you can "switch" to Elvish for some new scripts and still use bash as your interactive shell (and your existing scripts). If you want to give Elvish a try, you can just do it without giving up everything you do with bash today, and reap the incremental benefits of using a better shell.
On the other hand, the mental barrier of adopting "a new thing" is definitely real, even after you overcome the more irrational fears. We only want so much software in our lives anyway. To that I'd say Elvish needs to be more compelling than what it is today. Elvish today, with its nice interactive features and a real programming language, is perhaps 2x to 4x better than traditional shells on its own (if we ignore the whole ecosystem issue), but it needs to be more than that to stand out to people as something radically better. I'm still trying to figure out how to do that, but two ideas I'm exploring are:
- Make it super easy to build TUIs in Elvish - we've had a "renaissance" of TUI tools in recently years, but if you want to build a TUI from scratch it's still surprisingly complicated. Much simpler than building GUIs, but still much more complex than just building a CLI. I feel Elvish as a shell language is a really good place to have a nice TUI framework in.
- Make it super easy to manage a simple homelab or build farm in Elvish - this is traditionally a strength of shells and people still cobble together shell scripts for this purpose, but traditional shell languages are clumsy and you can't easily expose a nice UI from shell scripts. It would be very nice if you can compose some Elvish scripts together and have a nice TUI or a web UI for management at a very low extra cost.
I would spend time building a top notch LSP for Elvish. Shell scripting languages tend to be highly untyped and flexible so building a good LSP will be tough here. But if you were able to build one that works really well you would have another really compelling reason to use Elvish.
P.S. I am aware that Elvish has a built in LSP. It’s somewhat basic though IIRC.
That's interesting, because I have the same starting point as you (optimise hacked-up script experience) but a lot of the time as my script grows I really wish there's a nice little TUI for it. For example, rather than finding my invocation of the script from my command history and edit the CLI arguments, can I just have a little "pop-up menu" for my workflows, where I can probably add some string argument? Or allow me to see the outputs of the script in a slightly more fancy way than a stream of text - like a Miller column view, which Elvish already has implemented in its file manager? Ideally, these should be "ready-made" UI components that you can take advantage of from your hacked-up script with just one or two lines of code.
I'm also coming from the viewpoint of implementation - I've built Elvish's TUI using some very generic building blocks but they are only used for very specialized builtin features of Elvish, so I'm really keen on exposing the capability for shell scripts to use.
I guess we're thinking of different things when we say "TUI" - perhaps you are thinking of full blown TUI applications, like an editor? I'd agree that a more [1] general-purpose language is more suitable and Elvish is not in the business of being a good language to implement the next TUI editor :) Maybe I should coin a new term for what I have in mind, which should be something you can hack up as easily as a CLI. "TUIlet" maybe?
> I would spend time building a top notch LSP for Elvish. Shell scripting languages tend to be highly untyped and flexible so building a good LSP will be tough here. But if you were able to build one that works really well you would have another really compelling reason to use Elvish.
Definitely! It's not what I'm working on in the immediate future, but good editor integration is a must to make the experience of writing scripts much better than more traditional shells.
1. Emphasis on "more", because Elvish is a general-purpose language itself, just not the most general-purpose :)
In elvish I need to request an autocomplete— in fish I get a suggestion in a light font which is usually a good one.
Another nice thing in fish is that valid file names get automatically underlined. It’s another subtle but useful feature.
So I think you know what is coming. A feature request :-):
Please make Elvish prompt autocomplete/suggestions more like fish. I wouldn’t be surprised if this has not been requested already !
I can't promise anything, but I'm undertaking a major refactoring of Elvish's TUI and that should make it much easier to implement this feature in the near future.
Also when I started Elvish 10 years ago (!) PowerShell wasn't ported to non-Windows operating systems yet, so it wasn't even viable for most people.
(This is a really neat tool, great work!)
Elvish visibly leans into this to try to have a consistent syntax. Sometimes the similarities go deeper, too; IIRC, its number types are inspired by Scheme, for example.
Yes! Similarity to Lisp is very much a deliberate design choice and I feel happy every time a Lisper picks that up :)
I believe Elvish has also contributed a novel solution to the Lisp-1/Lisp-2 divide by making the space of command names equivalent to the subspace of variable names with a suffix - the command "foo" is equivalent to the variable "$foo~" and vice versa. (There's a bit more twist than that - the command "foo" can also resolve to the external command named "foo", but then that's represented by the "$e:foo~" variable.)
Powershell is not true FP, but, if you stretch terminology:
- the unit of feature delivery is the function - you can pass functions as values (but it's not idiomatic or elegant) - objects tend to look more like anonymous records than OOP classes with methods - pattern matching is supported (sort of)
I'm not keen on trading bashisms for elvishisms though :-) A somewhat random example from looking at the docs, where you have this example:
~> curl -s https://dl.elv.sh/$platform:os'-'$platform:arch/elvish-HEAD....
Quoting '-' instead of using (bash-style) "${platform:os}-${platform:arch}" seems really counterintuitive to me. After all, '-' is simply itself (a plain dash), it's the variable name that is special here and deserves quoting. Any thoughts on this?
~> curl -s https://dl.elv.sh/{$platform:os}-{$platform:arch}/elvish-HEA.......
It's more verbose than quoting the dash so I opted to the latter.
You seem to use "elvishism" in a negative way, and this particular example is one of the more quirky ones, but the whole reason you would use Elvish is that it's different from other shells! :)
So I'll definitely do more testing with Elvish and see what the overall experience is like. Coming from Solaris, we still have a ton of ksh scripts in our shop (with people working on them who have very different backgrounds and scripting skill levels). Something with less pitfalls than bash would be great... Thanks for putting in your time and effort!
It has the characteristics of a really great idea: seems obvious in retrospect, doesn’t look too difficult to implement, but somehow everybody seems to have… just missed it?
These days browsers will often suggest results from search engines but back then (at least in Firefox?) it was just history and bookmarks.
Is that doable?
Related: is there a comprehensive list of hotkeys floating around? I didn't see it in the docs. Things like Alt-Enter and Alt-, are mentioned in the Quick Start, but I couldn't find them elsewhere.
1) Maybe link at the top of the ref page to the learn page? I started looking at ref but some stuff I needed to know was much better explained under learn and it took me a while to notice that it was there also (I'm guessing the existance of ref would be more obvious from the learn page).
2) Local documentation is vital for a shell, if there is a way to generate a man page from the available documentation that would be quite helpful (at least instaling the website would be good, but that looks like a package issue that should be easy to do).
3) If I want the equivalent of fd -X but being able to use aliases (functions in elvish) there is from-terminated and (all) but despite the long command line lengths these days it can overflow. A builtin simplified version of xargs that works with elvish lists and aggregates arguments while breaking into acceptable length command lines would be great.
4) There seems to be no way to execute code from a different file in the current context? For bash to get aliases in fd -X you can use a complicated fd ... -X bash -ic '<alias> "$@"' 'fd bash', however it seems that elvish both doesn't have -i and has no way to even source the rc file from elvish -c? Or to split rc files into multiple files without using modules?
5) &> and to a lesser extent |& are handy in bash to simplify combining stderr and stdout. Hopefully elvish doesn't have the 2>&1 placement issues that posix shells do but even if not I'd consider a shorthand worthwhile.
6) Some type of cooperative locking would be helpful along the lines of Debian's lockfile-progs but built in to make it easy to avoid scripts that break if you accidently run it twice at the same time (ideally compatible with lockfile-progs unless there is a good reason not to be).
7) Beyond that, some type of built in concurrency managed database based on elivish data types might be worth considering to simplify sharing state between independent execution of related utilities (or multiple execution of a single utility).
8) I like your idea of exposing more TUI abilities. In addition to what you said, being able to create a status line or two while preserving the scrolling history of other commands would be helpful (and this would be potentially interesting in an interactive shell also). I like your rprompt.
Overall, I like the look of elvish and for some reason it seems more approachable to me (not a big fan of lisp) than the PowerShell style shells.
I can't speak for other people, but I made it on my own and don't have any formal training in design.
With the risk of stating the obvious, you first have to realize that as a developer you can make a reasonably clean-looking website on your own. There are just a few basic ingredients: fonts, spacing, positioning, background shades, and rounded corners. You can do all of these from CSS and there is a lot of good resources for CSS today.
After that it's studying other websites, replicating the style you like, and a lot of trial and error. You can do a lot of experiments from the browser's dev tool before committing them into the stylesheet too. But at the end of the day, you have to put in some time. The layout of the current homepage was redone just a few months back and it took me (IIRC) 3 days to tweak everything to my satisfaction.
> For the .sh file extension type, see shell script.
[0] https://marketplace.visualstudio.com/items?itemName=elves.el...
I don't see infix math though, which is one thing I'd really like to have in a shell, although maybe that better done with a one letter alias to Python or the like.
(at least I warned people I was undercaffeinated?)
peach {|h| ssh root@$h[name] $h[cmd] } $hosts
Does it harken back to some popular language? I first encountered it in nushell but now that I've seen it in elvish I'm wondering where it came from.Elvish predates Nushell by quite a few years BTW :)
It's one of the things that keeps me reaching for picolisp, the 'fork convenience function is quite nice for doing interactive parallel 'batching'.
for x in *.jpg; do
gm convert $x ${x#.jpg}.png
done
to for x [*.jpg] {
gm convert $x (str:trim suffix $x .jpg).png
}Re "cosmetic" - I'd agree that the extent syntax matters is more limited compared to semantics, but that limited extent can be a lot of the daily experience with a language! I've ported many bash scripts to Elvish and I still find it liberating to not have to write double quotes everywhere for my script to handle whitespaces correctly.
I see the basic concept for Elvish as PowerShell's language power, but with the more Unix-y sensibilities of traditional shells, plus, crucially, the human-friendly focus on rich built-ins for interactivity exemplified by fish.
I don't think a real-world implementation of that kind of idea is a 'merely cosmetic' innovation. There's real novelty in that synthesis. How to balance those inspirations is not obvious, and neither is how to fill the gaps between the big picture ideas and a usable tool.
Go use Elvish for a while and you'll see how much creativity has clearly gone into it. Hell, just browse the GitHub issues and you'll see.
Now I use ble.sh [1] and Oh My Bash [2] and Atuin [3] and I love it.
This is really a field where I feel standardization is the better path. It's a similar feeling I get when I observe the vast array of notetaking apps I see made and think here is a place where it would be better to pick one FOSS solution and contribute.
[1] https://github.com/akinomyoga/ble.sh
I feel the opposite -- I use a commercial note-taking app for the same reason I use bash. It kinda sucks, but it's on all my devices, and the extra work imposed by the suckiness is more than made up for by familiarity and by not having to invest any energy to make it work seamlessly across my devices.
It’s similar to Ruby.
I only realized the similarity with Smalltalk after that. I did learn a little Smalltalk many years ago but completely forgot about it, but it's possible that the knowledge was always somewhere in my subconscious brain.
var hosts = [[&name=a &cmd='apt update']
[&name=b &cmd='pacman -Syu']]
# peach = "parallel each"
peach {|h| ssh root@$h[name] $h[cmd] } $hosts
is hosts=( \
"host_a" "apt update" \
"host_b" "pacman -Syu" \
)
printf "%s\n" "${hosts[@]}" | xargs -L2 bash -c 'echo ssh root@$0 -- $*'
and ~> var project = ~/project
~> rm -rf $projetc/bin
compilation error: variable $projetc not found
is set -u
project=~/project
rm -rf $projetc/bin
bash: projetc: unbound variable
Now of course, there are a lot of edge cases in bash, especially around escaping and quoting, which are not handled in my crude examples.What annoys me with these new shells, is that they dont solve any issue of the real world.
Yes, your language is cute.
Yes it's safer and less error prone that bash, there's no denying that.
But it does not - and most likely will never - handle the trillion - arguably hacky - things that can are done in bash, because that would suddenly completely overbloat the language, multiply its complexity, etc.
Essentially that means it will always be some kind of niche toy with a couple of enthusiasts, while the rest of the world keep on sticking to bash, and overall nothing moved forward.
Now don't get me wrong, I'm all for writing toys. I also have hundreds of my own reimplementations of stuff just because it's fun to code, you learn stuff, etc. But I don't get the "I'm going to create a fancy website, build a community and post it on HN" vibe.
If you seriously think the shell scripting world need a refresher, then the real issue, and we all know it, is not the fanciness of the language. That's the fun part.
The real, actual work, is to provide a realistic path forward. Either by working on bash to improve its syntax to something more modern, maybe through `set` flags to keep backward compatibility. Or to write a transpiler to bash, but with safer and stronger semantics.
This is the actual path forward, and it's the _not fun_ part, it's grunt work, and nobody does it, meanwhile we can sit and use a shiny shell while complaining how bad and unsafe bash is.
I think we simply can't say for sure which path is better for the future of shells, and I'm quite excited by the fact that different projects are exploring different directions. I will just stick to the path I find best and won't try to convert you :)
They made something new and non-detrimental and are sharing it with the world. It’s fine if it’s not something you’re going to use. I won’t either, but I’m failing to see the point of ridiculing their effort.
Your example seems unfair, too. It’s not like that is the only use-case on the homepage.
foreach x [glob *.jpg] {
exec gm convert $x [regsub {\.jpg$} $x {}].png
}
(bonus, you can use `foreach x [glob *.jpg] y [glob *.png]` to iterate on more than one list at once)The truth is: if you choose to use something rarer than a ten-leafed clover, you better have massively good reasons to do so. Otherwise, there are already other languages with good subprocess integration; pretty sure Guile would do the trick too.
The "everything is a string" idea sadly turned out to not work that well for most people though, so Elvish has lists, maps, and first-class functions :)
Tcl lists are arrays (and kind of deques since https://core.tcl-lang.org/tips/doc/trunk/tip/625.md), dicts are the first-class maps we should have had since the beginning and apply allows to build closures around it.
I don't know about Elvish, but globs in bash aren't part of the loop syntax, they're their own thing, so this also just works:
for x in *.jpg *.pngIn other words, it's OK to type the first example on the command line and it'll work just fine (and if it won't, you'll be able to step in and fix). But running it as part of anything automated would be haphazard.
As I read this, the whole point is that you have something that feels quite familiar syntactically to a common shell, but sane. Kudos to the author!
- You're not quoting your variables properly to prevent splitting and globbing.
- In case there are no jpg files in the working directory, Bash will put the pattern itself (*.jpg) into the $x variable. You need to explicitly check that the file in the variable actually exists before working on it.
Modern shells do not have these footguns.
Indeed that's one thing Elvish has better defaults that I forgot to mention in https://elv.sh/learn/scripting-case-studies.html#jpg-to-png...., and now I've added that, thanks :)
Maybe you're not as traumatized by the thousand small cuts when writing traditional shell scripts as I, and that's fair :) However, I'd also invite you to read the rest of the examples, which go into the deeper advantages.
I'd also say that you don't have to "change my shell, have it deployed everywhere, and learn a new language" to start adopting Elvish! It's very easy to get Elvish (https://elv.sh/get/), and you can start using it on one machine, or for one script without "switching" 100%. Think of it as another tool you can use alongside whatever shell you are using (I'd also refer you to the other comment I posted: https://news.ycombinator.com/item?id=40317203).
https://elv.sh/learn/tour.html also has a big list of syntax comparison between bash and Elvish.
This looks very cool, and I've been having a lot of inspiration to write my own shell, but I want to make it vaguely compatible with my system.
POSIX compatibility might be overkill for the start.
Elvish is written in golang. I’m sure that’s going to be quite grokkable if you want to understand how a shell is written ?
Even if it's heavily commented, I learn better from debate, trade off analysis, or failure analysis.
To put it simply, I like argue and break things.
I understand why someone might not want to start there, but I think that could be a bit short-sighted. I think people are far too quick to dismiss code that's old. Yeah, it may have a lot of warts (for a lot of reasons, some more valid than others), but that doe not negate the value of understanding how something was implemented.
I'm also not suggesting this would be easy. There are some very complicated C codebases out there. I'm just saying that I disagree that there's no point in examining them.
The remaining parts are stuff about process and file semantics - you can find that in APUE, or really from reading manpages. You also need to learn the arcane VT100 escape sequences for the TUI, and there are plenty of articles for that too.
It looks like APUE might be the ticket to fill in some of my gaps though. Thank you for the suggestion.
Escape sequences should be fine (famous last words?).
You're welcome.