Hacker News new | past | comments | ask | show | jobs | submit login
Nsh: A fish/bash-like Posix shell in Rust (github.com/nuta)
179 points by Klasiaster on Oct 23, 2021 | hide | past | favorite | 50 comments



Looks like the goal of this project is to be a replacement for bash with improved interactivity. That seems reasonable, compared to what you can do with zsh or fish bash is far behind concerning interactivity.

While I personally think shells that abandon POSIX/bash compliance are much more interesting as those can implement a sane scripting language, sticking with bash but improving interactivity is probably going to make more users happy as bash is so ubiquitous.


> bash is far behind concerning interactivity.

Is it really? Projects like ble.sh [1] seem to pull off advanced features like syntax highlighting and enhanced completion with pure bash without the need for reimplementing a whole shell from the ground up.

1.: https://github.com/akinomyoga/ble.sh


Wow, that looks amazing! Especially being implemented in pure bash!


Totally agree, but having used zsh, fish and elvish as my daily driver shell (each for at least 1 month) I think that not having POSIX/bash compliance eventually becomes an annoyance.

I like zsh because it is mostly (entirely?) POSIX compliant, but adds extended features. I think that's the best approach to make it easier for people to start using your shell.

random note: back when the Go language was first released I wanted to try it out.. I started making a shell that I called GoSH. Didn't get very far, you could execute programs, change directories etc. It was fun! And taught me a lot about low level languages.


The need for complete POSIX scope has just never been an issue for me using zsh. I shebang all my scripts to bash (or even plain old sh if its for some stripped down container). I guess if I wanted a lot of functions to run tasks natively it would, but never had that need (but aware some might).


This project looks very young. It might evolve into a contender but even the document on Bash compatibility just says "TODO" and frankly Bash compatibility is going to be a multi-year project on its own.

For anyone after an upgrade path from Bash/Zsh then Oil shell is a lot more mature: https://www.oilshell.org/


> Fish is really neat but I prefer old-fashioned, traditional, and ergonomic shell syntax.

What does this really mean? Especially the ergonomic part?


I do not know what was meant, but for example when I have looked at fish, I have seen some features that appeared to be nice, but also some gratuitous changes to the core shell syntax that were not improvements.

For example, replacing "&&" and "||" with "and" and "or", is not an improvement. There are many other such changes that make the shell more verbose without bringing any advantage.

This also happened with the C shell, csh, which introduced some convenient new features for interactive use, but it also included a large number of gratuitous changes in the syntax that were not improvements.


Note fish supports "&&" and "||" as of version 3.0


> some gratuitous changes to the core shell syntax that were not improvements

> For example, replacing "&&" and "||" with "and" and "or", is not an improvement. There are many other such changes that make the shell more verbose without bringing any advantage.

In Fish, `and` was never a synonym for `&&`, and `or` was never a synonym for `||`. Fish's use of `and` and `or` also did not introduce any syntax that you won't find in Bash and friends. `and` and `or` in Fish are _commands_, and they use the _pre-existing syntax_ for _normal commands_.

This is absolutely an improvement, because it's strictly less syntax that allows you to do all of the same things with virtually no increase in the number of characters. `; or` is only a single character more than ` ||`, and `;or` is the same number of characters as ` ||`. This is a very bad example of ‘increased verbosity’.


What you say about fish "and" and "or" is correct, but this only shows that fish is even worse from this point of view than I have said, because "&&" is replaced by ";and" and "||" is replaced by ";or".

If you like this syntax, that is fine, but I do not like it, and I also find it illogical, because McCarthy "and" and McCarthy "or" are really alternatives to the sequential execution denoted by ";", not something that is done after a sequencing delimiter, so this and many other details like this have prevented me to switch to using fish, even if fish also has many other features that I like.

Also, I have never reached deep enough in the fish documentation to discover how you can write the equivalent of:

  cmd1 && cmd2 || cmd3 && cmd4 &
from traditional shells, when "and" and "or' are ordinary commands. I assume that you might need to use parentheses before "&".

The fact that not only the symbols have been substituted but they have also been made ordinary commands is a serious regression. Less syntax that removes your ability to write something is not an improvement.

For any shell wannabe, there is a very large part of the syntax used by ksh/zsh/bash that can be changed without problems, e.g. conditional structures, loops, variable and function definitions, various kinds of parameter expansions and expressions and so on.

None of those is specific for a shell language and it is likely that better replacements can be found.

On the other hand, for the syntax of a shell command list, which uses expansions, redirections and "|", "&&", "||", ";", "&", nobody has found yet a better alternative so any shell should better keep it unchanged.


> If you like this syntax, that is fine, but I do not like it, and I also find it illogical, because McCarthy "and" and McCarthy "or" are really alternatives to the sequential execution denoted by ";"

‘alternative’ is doing some interesting work here, as evidenced by the example you use in the text that follows. If `&&` and `||` themselves sequencing delimiters that additionally imposed conditions upon the exit statuses of the commands in the sequence while also re-emitting those statuses, `&&` and `||` wouldn't behave the way they do in your example, affecting the behavior of the `&` operator. This means that your example shows that `||` and `&&` are also in a distinct syntactic category from that of `;`. Alternatively, we can nominally put them in the same category, as Bash does by calling `;`, `||`, and `&&` ‘control operators’ (along with pipes and other things), but then we find ourselves in a situation where _some_ control operators modify the scope of any `&` operators that succeed them but other control operators don't. So how conceptually useful is this category, now?

> Also, I have never reached deep enough in the fish documentation to discover how you can write the equivalent of: > > cmd1 && cmd2 || cmd3 && cmd4 & >

I am glad this expression is not idiomatic Fish. `&&` and `||`, as command operators here, serve to give chaining some commands together some resemblance to boolean expressions in other programming languages. But would-be formula in first-order logic which captures the logical component of this expression

  a ∧ b ∨ c ∧ d
is not a well-formed formula! So what it means is a matter of convention. The role of the final `&` here is the same: yet another construct whose meaning depends on the script language author's choice of operator precedence.

By contrast, in Fish, the behavior you should expect falls out of the ordinary syntax for the commands and the manual's description of the builtin `and` and `or` commands. In Fish, this is also quite easy to look up, e.g., with `help and`, which yields:

> and is used to execute a command if the previous command was successful (returned a status of 0).

> and statements may be used as part of the condition in an while or if block.

> and does not change the current exit status itself, but the command it runs most likely will. The exit status of the last foreground command to exit can always be accessed using the $status variable.

which is all you need to understand how `and` works. By contrast, in Bash (which I'll continue to use as my example for the more conventional syntax and semantics), to figure out your example expression, you have to read this from the Bash docs (after reading the section on pipelines, of course):

> Lists > A list is a sequence of one or more pipelines separated by one of the operators ;, &, &&, or ||, and optionally terminated by one of ;, &, or <newline>. > > Of these list operators, && and || have equal precedence, followed by ; and &, which have equal precedence. > > A sequence of one or more newlines may appear in a list instead of a semicolon to delimit commands. > > If a command is terminated by the control operator &, the shell executes the command in the background in a subshell. The shell does not wait for the command to finish, and the return status is 0. These are referred to as asynchronous commands. Commands separated by a ; are executed sequentially; the shell waits for each command to terminate in turn. The return status is the exit status of the last command executed. > > AND and OR lists are sequences of one or more pipelines separated by the && and || control operators, respectively. AND and OR lists are executed with left associativity. An AND list has the form > > command1 && command2

> command2 is executed if, and only if, command1 returns an exit status of zero (success). > > An OR list has the form > > command1 || command2 > > command2 is executed if, and only if, command1 returns a non-zero exit status. The return status of AND and OR lists is the exit status of the last command executed in the list.

Okay, okay. So anything conjoined by `&&` and `||` is like one big command, since && and || have higher precedence than anything else. That's why the final `&` in your example applies to the whole list of pipelines that come before it.

Only that's not true! A look inside the manpage for bash's builtins reveals blurbs like this in the entries for `set` and `trap`:

> part of any command executed in a && or || list except the command following the final && or || > part of a command executed in a && or || list except the command following the final && or ||

which is the exact opposite of the behavior `&&` and `||` exhibit in your example when followed by `&`. Fantastic. Very ‘logical’.

To return to this:

> Also, I have never reached deep enough in the fish documentation to discover how you can write the equivalent of: > > cmd1 && cmd2 || cmd3 && cmd4 & >

Fish has blocks, so you'd write:

  begin
    cmd1
    and cmd2
    or cmd3
    and cmd4
  end &
or use a function. But the above example doesn't actually work, because unfortunately Fish has a deeper defect than a syntactical discrepancy here, and job control is not implemented for Fish functions, see https://github.com/fish-shell/fish-shell/issues/238 and the Fish manual's section on job control https://fishshell.com/docs/current/language.html?job-control.... But again, this is not a syntax issue but a job control issue, a defect in the implementation rather than the design of the syntax.

Finally:

> For any shell wannabe, there is a very large part of the syntax used by ksh/zsh/bash that can be changed without problems, e.g. conditional structures, loops, variable and function definitions, various kinds of parameter expansions and expressions and so on. > > None of those is specific for a shell language and it is likely that better replacements can be found.

This is actually interesting and constructive, because it gives a clearer picture of the kind of ‘improved’ shell you might want. I hope that one comes to be, if it's not already being worked on.

But I just disagree that the traditional command list structure is necessary or good. It's full of arbitrary conventions and gotchas, and the additional flexibility it gives you is trivial. It's a bummer that Fish's job control lets us down here, but that has nothing to do with the syntax.


I find typing `&&` to be annoyingly difficult. I have to hold shift and hunt around for the `7` key (and probably hit `6` or `8` first). Whereas I can type "and" without looking or even really thinking about it.


I have used fish for years, it is just painful to watch how other people work with old fashion shells.

I applaud this project for bringing fish goodness to more people, although I don’t quite get why people cannot just learn fish. Fish is very simple. There is not that much to learn.

Maybe it matters if your write a lot of shell scripts but for anything complicated I use Julia, Python, Go or some other more proper language.

To me shells are mainly for interactive work and really simple scripts without control flow, beyond perhaps a for loop.


As someone who stopped using fish, I found fzf to be more useful than fish’s history completion so it became less tolerable to have to type `env` in front of the bash oneliners our test runner puts out.


Just noting that fish supports the `var=val cmd` syntax starting in 3.1, so `env` shouldn't be necessary.


I didn’t know this! I’ll try installing fish again


how did I miss this??? I've been continuing to use the env command for this for more than a year now

womp womp


I've used fish in the last few years and zsh before that.

The experience out of the box (coupled with something like starship) is pretty good but I don't like the incompatibility with basic things, like $() vs ().

I also find fish to be a bit on the slow side in some instances, so I think I'll keep tabs on nsh


> I don't need customizability

Configuration isn't for you. It's for other people. Configuration is the part of a program that performs the function of making it useful for more than one person. It is the necessary glue that brings together many developers and many users into one project.


Software that requires configuration to be useful to more than one person has failed it's mandate in proportion to that configurability. It's precisely the opposite of necessary glue, it's duct tape: often the pragmatic choice, but almost always a sign of a design failure.


What utter tosh. People have different preferences because people are different. If your point was apt then this submission wouldn’t exist because $SHELL wouldn’t be a configurable option in the first place.


People have different preferences, but moreover people have different needs. Configuration shouldn't be thought of as optional. If, as the parent comment says, settings are design failures, they arise from the fact that it's not possible to design software that works for everyone. To fix this problem you wouldn't have to design better software, you'd have to design a better human!

Just to state the obvious examples, i18n is exposed through configuration, accessibility features are configurations. These aren't optional parts of a program that can simply be designed around.

To be clear, I agree with you that human preferences are valid as such, just clarifying that your parent comment is profoundly wrong even if you think that all people ought to have the same preferences.


Yes, ideally, there would be one shell used everywhere. Obviously!


We wouldn’t have Rust either because why develop a new programming language when we already have programming languages?

I’m fact why even have Linux or macOS running atop x86 when we have perfectly good z80 micro computers running a BASIC interpreter…

I’m all for difference of opinions but that is clearly the dumbest one posted to HN in a long time.


Optionality exists because we're still zeroing in on the ideal.


Is that true for ... anything at all, though? Outside of computer programs, where you claim it is? The ideal food, the ideal movie, the ideal vacation, the ideal video game, the ideal body, the ideal sofa, the ideal tree...

Choice arises because humans are different, and want - and need! - different things.


There is no ideal food. There is an ideal shell. The difference is that the goals of a shell are well defined, and invariant to human preference. Human preference is real! But when it comes to computer systems it's rarely the correct variable to be maximizing in the optimization equation. The most wonderful property of humans is that we are adaptable: if a circumstance requires us to think of things in a model that isn't our intuitive choice, because the nonintuitive model produces better outcomes by the meaningful metrics, then we are capable of doing that! Our preferences are not these sacrosanct things that dominate the design calculus. They can and should take a backseat to other things when appropriate.


No, it exists because people depend on different use cases.

There’s no such thing as a one size fits all. To suggest there is only demonstrates how little exposure you’ve have to the wider field of computing.


The name sounds very similar to Nushell[1], also written in Rust, but more mature and recognizable

1: https://www.nushell.sh/


But less compatible, which is the goal here. Being a practical shell to use


Very cool. I really love fish but sometimes find it very annoying to translate random posix snippets from the web.


The trick is to enter bash from fish, run your command, and then call fish again.

    (fish)$ bash
    (fish>bash)$ source ./random.sh
    (fish>bash)$ fish
    (fish>bash>fish)$


I think you should hit ctrl+d when you are done in bash to avoid the nested fish>bash>fish.


The idea is to return to a fish shell, but with all the env-vars that bash might have loaded as a result of executing the script.


`exec <shell>` would avoid nested shells and inherit the environment. Which might be preferable if you find your terminal emulator complaining about processes still running on close.


Would it be better to ‘exec’ the shells to avoid the nesting?


Or just simply:

    (fish)$ bash -c ./random.sh


That doesn't give you the env vars that the script might have set.


You can use fenv or bass for that. They run a snippet or script of bash in bash, then import all the environment changes for you

https://github.com/oh-my-fish/plugin-foreign-env

https://github.com/edc/bass


could you explain this? How could the script set env vars before you run it??


No need to use -c there:

    (fish)$ bash random.sh


Have you made use of tools like babelfish? https://github.com/bouk/babelfish


With Fish 3.0 and 3.1 it’s even more compatible with snippets you are likely to find to copy and paste. Do you remember something you found that doesn’t work?


Unless it is really simple stuff why not just drop temporarily into bash?

I mainly use fish for interactive work not for programming. Then I use a real programming language.


There is also https://github.com/nixpulvis/oursh which wants to be POSIX compatible with its own extensions. Actually I would prefer if a new shell focuses on bash compatibility.

If features are integrated, then I would probably want to have something about error handling because it is difficult to get right in bash even with shellcheck¹ plus shellharden², and gets just worse when things run in parallel.

¹ https://www.shellcheck.net/

² https://github.com/anordal/shellharden


I'm actually most interested in the UX around https://explainshell.com, but I haven't had the time to dig into it yet still.


Just a heads up: name collision with the default shell on (now Apache) Nuttx.

http://nuttx.incubator.apache.org/docs/latest/applications/n...


Will this support native windows? The main reason I have switched to nushell is because it is cross platform


Nice! I see nsh uses threads internally. Any plans on how to handle the fork-thread interaction?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: