Nushell 0.78

Nushell, or Nu for short, is a new shell that takes a modern, structured approach to your command line. It works seamlessly with the data from your filesystem, operating system, and a growing number of file formats to make it easy to build powerful command line pipelines.

Today, we're releasing version 0.78 of Nu. This release adds pattern matching, speed improvements, easier cell paths when columns may be missing, better error handling, and much more.

Where to get it

Nu 0.78 is available as pre-built binariesopen in new window or from crates.ioopen in new window. If you have Rust installed you can install it using cargo install nu.

NOTE: The optional dataframe functionality is available by cargo install nu --features=dataframe.

As part of this release, we also publish a set of optional plugins you can install and use with Nu. To install, use cargo install nu_plugin_<plugin name>.

Themes of this release / New features

Pattern matching (sophiajt)

With 0.78, Nushell now comes with the match expression, which allows you to do pattern matching on a value. It supports a variety of different kinds of patterns, which can be mixed and matched to create more complicated patterns.

A simple example matching against a constant or a range:

match 3 {
  1 => { print "it's a 1" }
  1..10 => { print "it's between 1 and 10" }

Another example, this time matching against a record value, with a fall-through if it doesn't match:

match $a {
  {x: $x, y: 10} => { print $"y is: 10 and x is: ($x)" }
  _ => { print "the above didn't match, so we print this instead" }

You can also match on the elements of a list:

match $list {
  [$one] => { print "one element list" }
  [$one, $two] => { print "two element list" }
  [$head, ..$tail] => { print $"the tail of the list is ($tail)" }

Alias changes (kubouchopen in new window)

Aliases now can shadow

We've updated the new alias command we introduced in 0.77 to work more closely to the previous one. For example:

> alias l = ls -a
> alias l = ls -l

Will now create the l alias that points to ls -l, with most recent line getting precedence during alias expansion.

Alias can be named the same as the aliased command

> alias ls = ls -a

Now correctly displays all files when calling ls, instead of throwing an unhelpful error.

Old alias still keeps working

Since there are still some remaining issues to fix with the new alias implementation, we still keep old-alias around for this release.

Speed improvements (sophiajtopen in new window)

We've sped up the performance of tight loops like for and while considerably in this release. For example, on our test machine:

Example 1: timeit { for x in 1..1000000 {} }

  • 0.77.1: 908ms
  • 0.78.0: 52ms

Example 2: timeit { mut x = 1; while $x < 1000000 { $x += 1 } }

  • 0.77.1: 1082ms
  • 0.78.0: 170ms

Optional cell path members (rgwoodopen in new window)

In Nu 0.78, you can use ? in cell paths to suppress errors from missing data. ? is a more convenient+powerful version of the -i/--ignore-errors flag on get and select. Here are some examples:

{ foo: 123 }.bar # errors because `bar` is not present on the record
{ foo: 123 }.bar? # returns null

{ foo: 123 } | get bar # errors
{ foo: 123 } | get bar? # returns null

{ foo: 123 }.bar.baz # errors
{ foo: 123 }.bar.baz? # errors because `bar` is not present
{ foo: 123 }.bar?.baz # returns null even though `baz` is not present; `?` short-circuits
{ foo: 123 }.bar?.baz? # returns null

[1, 2, 3].8 # errors because there is no 8th item in the list
[1, 2, 3].8? # returns null

[{foo: 123}, {}].foo # errors because `foo` is not present on every item in the table
[{foo: 123}, {}].foo? # returns a list [123, null]

? works anywhere that cell paths work, including where:

> [{foo: 123}, {}] | where foo? == 123
 # │ foo │
 0 123

better error handling in error make (amtoine in #8511open in new window and #8570open in new window)

The error make command now gives better hints about why the format is not a valid error make format:

  • with an empty format, error make {} will say that there is a "missing required member $.msg"
  • with an empty $.label, error make {msg: "message", label: {}} will say there is a "missing required member $.label.text"
  • finally, when $.label.start / $.label.end is not defined while the other is, error make will give a hint as to add the missing one to the format!

The second change disallow the use of a $.label.start greater than $.label.end as a span.

Support for pretty output format in to nuon (amtoineopen in new window)

The to nuon command can now output pretty NUON data with whitespaces for better readability.

The default behaviour still is to output everything on a single line, encouraging users to leverage the compactness of the NUON data format.

However, we can now output formatted NUON data with the --indent and --tabs options:

> [1 2 3] | to nuon --indent 4


> {date: 2000-01-01, data: [1 [2 3] 4.56]} | to nuon --indent 4
    date: 2000-01-01T00:00:00+00:00,
    data: [

The default single-line behaviour still can be enforced with the --raw option, e.g.

> {date: 2000-01-01, data: [1 [2 3] 4.56]} | to nuon --indent 4 --raw
{date: 2000-01-01T00:00:00+00:00, data: [1, [2, 3], 4.56]}

New math exp command (lesvesopen in new window)

To complement the math ln command, we now include a math exp command for exponentiation with the base e.

Breaking changes

let requires surrounding parentheses for saving command output

let x = ls will not run the ls command anymore. If you need to save the output of a command, you need to wrap it in parentheses: let x = (ls).

|| now required in closures

To help differentiate between blocks (which can mutate variables) and closures (which can be used in a pipeline), we've changed the syntax of closures to require ||. This means the simplest closure now looks like {|| }

We no longer automatically print values as part of a script

We've changed the automatic-printing rules for scripts and the REPL to now only automatically print the last value. This means two major breaking changes:

Bare words can't start with a number

Words that start with a number or are in some way number-like must now must be wrapped in backticks to be treated at a bare word or wrapped in quotes to be a stringopen in new window.

Fields can only be defined once

You can no longer redefine a field in a record during initializationopen in new window

Thread configuration moves to par-each

Nushell no longer accepts the -t/--threads flag to the binary as it's now part of par-eachopen in new window

Ranges become the standard range specifier

str substring now only accepts rangesopen in new window as does bytes atopen in new window.

Alias recursion has been disabled

Alias recursion is now disabledopen in new window

Empty lists handled more consistently

[] | select foo now returns an empty list instead of nullopen in new window and sort, uniq, sort-by, and uniq-by now return empty lists when given an empty listopen in new window (previously they would throw an error)

These changes make it easier to work with lists of unknown size.

Comparison operators now allow null

Previously expressions like 1 < null would error; now they return nullopen in new window.

Full changelog