Live data from Hacker News

Hush, a modern shell scripting language

hush-shell.github.io

111–120 of 192 posts

Re: Hush, a modern shell scripting language

#111

If this wants to sell itself as a shell scripting language, it should very quickly advertise what is it that makes it superior to say, bash for typical shell scripting tasks. Shell scripts with bash are painful to the point that if I find myself writing more than around 10 lines of shell, I tend to stop and switch to Perl instead. But Perl hasn't been too popular lately and isn't ideal either, so I'm very much up for…

Shameless plug, but the problems you've described are exactly the problems I was looking to solve with my own shell (https://murex.rocks / https://github.com/lmorg/murex)

> 100% reliable argument passing. That is, when I run `system("git", "clone", $url);` in Perl, I know with exact precision what arguments Git is going to get, and that no matter what weirdness $url contains, it'll be passed down as a single argument. Heck, make that mandatory.

Variables are parsed as tokens so they're passed to the parameters whole, regardless of whether they contain white space or not. So you can still use the Bash terseness of parameters (eg `git clone $url`) but $url works in the same way as `system("git", "clone", $url);` in Perl.

> 100% reliable file iteration. I want to do a "for each file in this directory" in a manner that doesn't ever run into trouble with spaces, newlines or unusual characters.

Murex is inspired by Perl in that $ is a scalar and @ is an array. So if you use `f +f` builtin (to return a list of files), it returns as a JSON array. From there you can use @ to expand that array with each value being a new parameter, eg

  rm -v @{ f +f } # delete all files
or use $ to pass the entire array as a JSON string. Eg

  echo ${ f +f }
  # you could just run `f +f` without `echo` to get the list.
  # This is just a contrived example of passing the array as a string.
Additionally there are lots of tools that are natively aware of arrays and will operate on them. Much like how `sed`, `grep`, `sort` et al treat documents as lists, murex will support lists as JSON arrays. Or arrays in YAML, TOML, S-expressions, etc.

So you can have code that looks like this:

  f +f | foreach file { echo $file }
> No length limits. If I'm processing 10K files, I don't want to run into the problem that the command line is too long.

This is a kernel limit. I don't think there is any way to overcome this without using iteration instead (and suffering the performance impact form that).

> Excellent path parsing. Such as filename, basename, canonicalization, finding the file extension and "find the relative path between A and B".

There are a number of ways to query files and paths in murex:

- f: This returns files based on meta data. So will pull files, or directories, or symlinks, etc depending on the flags you pass. eg `+f` would include files. `+d` would be include directories. or `-s` would exclude symlinks.

- g: Globbing. Basically `*` and `?`. This can run as a function rather than being auto-expanded. eg `g *.txt` or `rm -v @{ g *.txt }`

- rx: Like globbing but using regexp. eg `rx '\.txt$'` or `rm -v @{ rx '\.txt$' }` - this example looks terrible but using rx does sometimes come in handy if you have more complex patterns than a standard glob could support. eg `rm -v @{rx '\.(txt|rtf|md|doc|docx)$'}`

The interactive shell also have an fzf like integration built in. So you can hit ctrl+f and then type a regexp pattern to filter the results. This means if you need to navigate through complex source tree (for example) to a specific file (eg ./src/modules/example/main.c) you could just type `vi ^fex.*main` and you'd automatically filter to that result. Or even just type `main` and only see the files in that tree with `main` in their name.

> Good error handling and reporting / Excellent error reporting. I don't want things failing with "Command failed, aborted". I want things to fail with "Command 'git checkout https://....' exited with return code 3, and here's for good measure the stdout and stderr even if I redirected them somewhere".

A lot of work here.

- `try` / `trypipe` blocks supported

- `if` and `while` blocks check the exit code. So you can do

  if { which foobar} else {
    err "foobar does not exist!"
  }

  # 'then' and 'else' are optional keywords for readability in scripting. So a one liner could read:
  # !if {which foobar} {err foobar does not exist!}
- built in support for unit tests

- built in support for watches (IDE feature where you can watch the state of a variable)

- unset variables error by default

- empty arrays will error by default when passed as parameters in the next release (hopefully coming this week)

- STDERR is highlighted red by default (can be disabled if that's not to your tastes) so you can clearly see any errors if they're muddled inside STDOUT.

- non zero exit numbers automatically raise an error. All errors return a line and cell number so you can find the exact source of the error in any scripts

Plus lots of other stuff that's referenced in the documents

> Easy capture of stdout and stderr, at the same time. Either together or individually, as needed.

Murex handles named pipes a little differently, they're passed as parameters inside triangle brackets, . STDERR is referenced with a proceeding exclamation mark. eg a normal command will appear internally as:

  command1   parameter1 parameter2 etc | command2   parameter1 parameter2 etc
(You don't need to specify nor for normal operation).

So if you want to send STDERR off somewhere for later processing you could create a new named pipe. eg

  pipe example # creates a new pipe called "example"
  command1  parameter1 parameter2 | command2  parameter1 parameter2
This would say "capture the STDERR of the first command1 and send it to a new pipe, but dump the STDERR of command2".

You can then later query the named pipe:

   | grep "error message"
> Give me helpers for common situations. Eg, "Recurse through this directory, while ignoring .git and vim backup files". Read this file into an array, splitting by newline, in a single line of code. It's tiresome to implement that kind of thing in every script I write. At the very least it should be simple and comfortable.

This is where the typed pipelines of murex come into their own. It's like using `jq` but builtin the shell itself and works transparently with multiple different document types. There's also an extensive library of builtins for common problems, eg `jsplit` will read STDIN and output an array split based on a regexp pattern. So your example would be:

  cat file | jsplit \n
> That's the kind of thing I care about for shell scripting. A better syntax is nice, but actually getting stuff done without having to work around gotchas and issues is what I'm looking for.

I completely agree. I expect this shell I've created to be pretty niche and not to everyone's tastes. But I'd written it because I wanted to be a more productive sysadmin ~10 years ago and since I've moved into DevOps I've found it invaluable. It's been my primary shell for around 5 years and every time I run into a situation where I'm like "I wish I could do this easily" I just add it. The fact that it's a typed pipeline makes it really easy to add context aware features, like SQL query support against CSV files.

Re: Hush, a modern shell scripting language

#113

Earlier quoted context omitted.

> Here Well... there is still no leak as there is no new shell spawn. I personally find this behavior way more natural then that of bash. If you don't want it, use variables. I can imagine coming from bash background that this irritates you. > WinMain gets the entire command line as a single string This has nothing to do with PowerShell which imposes its own parameter parsing standard so that no individual scripts do…

> Well... there is still no leak as there is no new shell spawn. I personally find this behavior way more natural then that of bash. If you don't want it, use variables. I can imagine coming from bash background that this irritates you. It's a serious annoyance when you're doing build scripts that do stuff like setting $PATH. Suddenly, stuff breaks randomly depending on what you ran in that particular powershell wind…

> It's a serious annoyance when you're doing build scripts that do stuff like setting $PATH.

Indeed, I was bitten by this too. Honestly, its probably the best not to touch PATH and friends and resolve the problem some other way. Or you could use modules.

> It also has the annoying "feature" of leaving you in the last directory the script changed to.

Yeah, that might be problem. I used to use cd instead of cpush because of this. However, using good framework fixes this. Check out Invoke-Build for your build stuff and you will forget about all of that. Its totally epic.

> It may mean there's no way whatsoever to pass a given path to a program. If a program internally doesn't handle quoting and spaces right, you're screwed.

Regarding space, this is easily solvable by using short syntax. Regarding quoting, this seems like a less important case - its very rare to have quotes in the file names in my experience.

Re: Hush, a modern shell scripting language

#114

Earlier quoted context omitted.

I love Python, but if all I need to do is grab one part of one line of something, and put it into some other command, then I'm just going to use some unholy mixture of sed, awk, or whatever. If I did the same thing in Python, I'd end up with thirty lines or more.

Thirty lines? Unholy one liners are not limited to shell scripting: from subprocess import check_output sh = lambda script: check_output(script, shell=True, text=True) ips = set(line.split()[-1] for line in sh('last -a').splitlines() if line and 'tmux' not in line) The first two lines are pure overhead. The last line is the equivalent of a shell script one liner but now has all the advantages of a language that suppo…

https://xon.sh/ would make this so much better

Re: Hush, a modern shell scripting language

#115

If this wants to sell itself as a shell scripting language, it should very quickly advertise what is it that makes it superior to say, bash for typical shell scripting tasks. Shell scripts with bash are painful to the point that if I find myself writing more than around 10 lines of shell, I tend to stop and switch to Perl instead. But Perl hasn't been too popular lately and isn't ideal either, so I'm very much up for…

Shameless plug, but the problems you've described are exactly the problems I was looking to solve with my own shell ( https://murex.rocks / https://github.com/lmorg/murex ) > 100% reliable argument passing. That is, when I run `system("git", "clone", $url);` in Perl, I know with exact precision what arguments Git is going to get, and that no matter what weirdness $url contains, it'll be passed down as a single argume…

That looks neat! I'll check it out.

Re: Hush, a modern shell scripting language

#116
A few things I commonly do in bash that it isn't clear (or wasn't immediately clear from the documentation that you can do in Hush):

Pass environment variables into a single command ala `NO_COLOR=1 ./foo` in standard sh. The only workaround I see is std.export to set it, and then std.export again to unset it.

The section on expansion doesn't mention std.glob - since there are times when you want to handle globs in variables it might be worth mentioning it there.

Some equivalent to getopt for argument parsing

Re: Hush, a modern shell scripting language

#117
post #59

PowerShell is really good. Like, amazingly good.

Yes, I find it odd how there's a massive blind spot when it comes to powershell. Proponents of strong typing everywhere in their programming languages are put off by a shell that dares pipe objects rather than having everything as strings. I get that Unix has a long tradition and it's hard to change, but powershell is genuinely a modern shell that while uncomfortably verbose without aliases is extremely powerful and…

I think a lot of it boils down to knee-jerk reactions about MS products. When I speak of powershell with colleagues, their first reaction is usually "I couldn't use a non-free shell!"... Even though powershell has been MIT licensed and open source for years now.

As for the verbosity, this isn't a good argument. In my mind there are two "modes" of powershell:

* Day to day shell use, which should definitely use aliases. Nobody wants to type Get-ChildItem to get the list of files in a folder ten times in a row.

* Script writing, which should use the long form of commands. Any good text editor (e.g. vscode does it) should be able to translate aliases into long form commands.

I think they really hit the sweet spot when considering these two aspects. Long scripts are readable without referring to manpages all the time, while day to day shell is quick and easy.

Re: Hush, a modern shell scripting language

#118

Earlier quoted context omitted.

> it's more unix than unix. Would you please elaborate what exactly do you mean by this?

Powershell improves on Unix commands being based on text streams, and makes them based on objects. Which means you're pretty much never extracting stuff with cut and awk, and instead can just get whatever field you want. Eg: C:\Windows> Get-AuthenticodeSignature .\explorer.exe Directory: C:\Windows SignerCertificate Status StatusMessage Path ----------------- ------ ------------- ---- BBD2C438000344F439BFDFE5ABAC3223…

> Powershell improves on Unix commands being based on text streams, and makes them based on objects

I assume this means that PowerShell is deeply integrated with the .NET ecosystem? I don't like .NET very much, so that's a downside for me.

One of the reasons I use Bash is the decades-old ecosystem of various utilities people have written. Can PowerShell use the same CLI utilities as Bash, and do the plain-old-text-stream processing pipelines like Bash, or is it .NET objects only?

Re: Hush, a modern shell scripting language

#119

Earlier quoted context omitted.

It blows my mind too. Typical verbosity argument is nonsense - people should use aliases. With cross platform shell, first time we have some reason not to use them, to make script more portable. Besides, this verbosity is form of documentation - you really must consider that any bash script comes with invisible man that you usually must check even after years of usage. When you factor that in, even fully verbose Powe…

> I have never seen good argument against PowerShell As long as I have to type `Get-Content` instead of `cat`, that's argument enough for me.

Good news: cat is a built-in alias for Get-Content in powershell.

Re: Hush, a modern shell scripting language

#120

Earlier quoted context omitted.

Yes, I find it odd how there's a massive blind spot when it comes to powershell. Proponents of strong typing everywhere in their programming languages are put off by a shell that dares pipe objects rather than having everything as strings. I get that Unix has a long tradition and it's hard to change, but powershell is genuinely a modern shell that while uncomfortably verbose without aliases is extremely powerful and…

I think a lot of it boils down to knee-jerk reactions about MS products. When I speak of powershell with colleagues, their first reaction is usually "I couldn't use a non-free shell!"... Even though powershell has been MIT licensed and open source for years now. As for the verbosity, this isn't a good argument. In my mind there are two "modes" of powershell: * Day to day shell use, which should definitely use aliases…

> Any good text editor (e.g. vscode does it) should be able to translate aliases into long form commands.

It can be done with script Expand-Alias[1] that can be used even as CI/CD action so people don't have to think about this.

I prefer to look into longer code in its aliased form..... mhm... we definitely need Unexpanding...

[1]: https://github.com/WormieCorp/Wormies-AU-Helpers/blob/develo...

Post reply on HN