Nushell 0.88.0

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.88.0 of Nu. This release adds a spread operator, output stream improvements, dynamic switch support, and much more.

Where to get it

Nu 0.88.0 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>.

Table of content

Themes of this release / New features [toc]

Hall of fame [toc]

Bug fixes [toc]

Thanks to all the contributors below for helping us solve issues and bugs 🙏

authordescriptionurl
@dead10ckopen in new windowinto binary -c: return 0 as single byte#11068open in new window
@zhiburtopen in new windowFix #11047#11054open in new window
@fdncredopen in new windowtweak table example/parameter text#11071open in new window
@fdncredopen in new windowadd "default" table theme#11072open in new window
@fdncredopen in new windowcorrect table example syntax#11074open in new window
@fdncredopen in new windowoptimize/clean up a few of the table changes#11076open in new window
@sophiajtopen in new windowFix the output type for 'view files'#11077open in new window
@danielsomerfieldopen in new windowFix toolkit to run workspace on 'check pr' (issue #10906)#11112open in new window
@hustceropen in new windowExit the release job if creating binary package failed#11145open in new window
@hustceropen in new windowFix release and nightly build workflow#11146open in new window
@amtoineopen in new windowfix the link to the nu_scripts in std clip deprecation#11150open in new window
@NotLebedevopen in new windowCp target expansion#11152open in new window
@MarikaChlebowskaopen in new windowAdd metadata to some filters#11160open in new window
@sholderbachopen in new windowRevert "Adding support for Polars structs"#11171open in new window
@sigodenopen in new windowFix spans passed to external_completer#11008open in new window
@nibon7open in new windowAdd boundary check for str index-of#11190open in new window
@WindSoilderopen in new windowWhen using redirection, if a command generates non-zero exit code, the script should stop running#11191open in new window
@nibon7open in new windowFix span of invalid range#11207open in new window
@nibon7open in new windowFix capacity overflow caused by large range of ports#11210open in new window
@IanManskeopen in new windowFix get -i ignoring errors for only the first cellpath#11213open in new window
@dtolnayopen in new windowFix Option<&str> == Option<&String> build error when using rust_decimal/rkyv feature#11205open in new window
@nibon7open in new windowAdd checks for ports#11214open in new window
@ysthakuropen in new windowFix highlighting of spread subexpressions in records#11202open in new window
@nibon7open in new windowFix overlay_use_main_not_exported hanging when an external spam command exists#11261open in new window
@AucaCoyanopen in new window🐛 Fixes markdown formatting on LSP hover#11253open in new window
@MarikaChlebowskaopen in new windowAdd more descriptive error message when passing list to from_csv#10962open in new window
@sophiajtopen in new windowRespect non-zero exit code in subexpressions and blocks#8984open in new window
@WindSoilderopen in new windowError on use path item1 item2, if item1 is not a module#11183open in new window
@KAAtheWiseGitopen in new windowMatch ++= capabilities with ++#11130open in new window
@fdncredopen in new windowAdd nu lib dirs default#11248open in new window

Enhancing the documentation [toc]

Thanks to all the contributors below for helping us making the documentation of Nushell commands better 🙏

authordescriptionurl
@sholderbachopen in new windowCurate developer documentation in tree#11052open in new window
@IanManskeopen in new windowBuild nu-protocol docs with all features enabled#11180open in new window
@poliorceticsopen in new windowfeat: Add default docs for aliases, generated from the command they point to#10825open in new window
@amtoineopen in new windowfix nu-std README#11244open in new window

Working on internals [toc]

Thanks to all the contributors below for working on internals of Nushell, such as refactoring the code 🙏

authordescriptionurl
@fdncredopen in new windowbump rust-toolchain to 1.72.1#11079open in new window
@nibon7open in new windowApply nightly clippy fixes#11083open in new window
@drbrainopen in new windowConvert ShellError::CommandNotFound to named fields#11094open in new window
@drbrainopen in new windowConvert ShellError::NetworkFailure to named fields#11093open in new window
@drbrainopen in new windowAdd Argument::span() and Call::arguments_span()#10983open in new window
@IanManskeopen in new windowRefactor Value cell path functions to fix bugs#11066open in new window
@sholderbachopen in new windowBump version to 0.87.2#11114open in new window
@sholderbachopen in new windowBump procfs to 0.16.0#11115open in new window
@drbrainopen in new windowRemove ShellError::FlagNotFound#11119open in new window
@drbrainopen in new windowConvert ShellError::AliasNotFound to named fields#11118open in new window
@drbrainopen in new windowConvert FileNotFound to named fields#11120open in new window
@hustceropen in new windowUpgrade Nu and script for release workflow#11121open in new window
@sholderbachopen in new windowUse record API in describe --detailed#11075open in new window
@drbrainopen in new windowConvert FileNotFoundCustom to named fields#11123open in new window
@drbrainopen in new windowConvert PluginFailedToLoad to named fields#11124open in new window
@drbrainopen in new windowConvert PluginFailedToEncode to named fields#11125open in new window
@drbrainopen in new windowConvert PluginFailedToDecode to named fields#11126open in new window
@sholderbachopen in new windowMove more commands to opaque Record type#11122open in new window
@rfaulhaberopen in new windowBump sysinfo to 0.29.11#11166open in new window
@drbrainopen in new windowConvert more ShellError variants to named fields#11173open in new window
@WindSoilderopen in new windowadd echo_env_mixed testbin to reduce windows only tests#11172open in new window
@cosineblastopen in new windowRemove file I/O from tests that don't need it#11182open in new window
@drbrainopen in new windowConvert more ShellError variants to named fields#11222open in new window
@nibon7open in new windowExplicitly indicate duplicate flags#11226open in new window
@ysthakuropen in new windowReduce code duplication in eval.rs and eval_const.rs#11192open in new window
@ayax79open in new windowUpgrading to polars 0.35#11241open in new window
@IanManskeopen in new windowRemove unnecessary boxing of Stack::recursion_count#11238open in new window
@schrieveslaachopen in new windowUpgrade lsp-server Dependency#11252open in new window
@drbrainopen in new windowConvert Shellerror::GenericError to named fields#11230open in new window
@sholderbachopen in new windowBump version to 0.87.1#11056open in new window
@dead10ckopen in new windowTesting support tweaks: exit status in Outcome#10692open in new window
@nibon7open in new windowSimplify clear implementation#11273open in new window
@drbrainopen in new windowConvert remainder of ShellError variants to named fields#11276open in new window

New spread operator for list and record literals [toc]

#10598open in new window asked for a spread operator (...), and in #11006open in new window and #11144open in new window, @ysthakuropen in new window implemented a spread operator in lists and records, respectively. Spreading arguments to commands has not been implemented yet.

The spread operator can help you avoid append in lists, e.g., this:

let list = ["foo"]
[
    1,
    ...$list,
    ...("foo" | split chars),
    ...[3 9],
    []
] | to nuon

will give:

[1, foo, f, o, o, 3, 9, []]

In records, it can help you avoid merge, e.g., this:

let record = { a: "foo" }
{
    ...$record,
    y: "bar",
    ...{ z: 2 }
} | to nuon

will give:

{a: foo, y: bar, z: 2}

You can spread variables, subexpressions, and either lists (inside list literals) or records (inside record literals). Note that there needs to be no whitespace between the ... and the expression being spread.

Passing boolean switches dynamically [toc]

In last release, a distinction has been introduced between switches, e.g. --enable-feature, and boolean options, e.g. --do-something: bool. The former is simply used as --enable-feature and the latter expects a value as --do-something false.

Up until now, this had the downside of disallowing one to pass boolean switches to inner calls of commands. An example would be two commands, foo and bar, which both have a --switch and where foo is calling bar internally with the opposite switch. It would have to look something like

def bar [--switch] {
    print $"switch in bar: ($switch)"
}

def foo [--switch] {
    print $"switch in foo: ($switch)"
    if $switch {
        bar
    } else {
        bar --switch
    }
}

or bar would have to take a boolean value instead of a switch, which would make the command less nice to use as part of a public API...

def bar [--switch: bool] {
    print $"switch in bar: ($switch)"
}

def foo [--switch] {
    print $"switch in foo: ($switch)"
    bar --switch (not $switch)
}

In this release and thanks to the work of @WindSoilderopen in new window in #11057open in new window, boolean switches can now be passed around. This will simplify the code above and still preserve the nice signature of bar :partying:

def bar [--switch] {
    print $"switch in bar: ($switch)"
}

def foo [--switch] {
    print $"switch in foo: ($switch)"
    bar --switch=(not $switch)
}

Redirection to standard streams is getting better [toc]

Once again, @WindSoilderopen in new window has been working on the redirection system of Nushell, fixing a bug and adding a new very nice feature:

  • #10851open in new window: one pipeline output stream can be redirected and the other one used as input to the rest of the pipe. Given the following Bash script called foo.sh that outputs both to stdout and stderr
    echo aaaaa
    echo bbb 1>&2
    echo cc
    
    it is now possible to run the following command, redirecting bbb to a file and the rest to the lines | each { ... } part of the pipeline:
    bash test.sh err> err.txt | lines | each { str length}
    
    resulting in [5, 2] as expected and bbb inside err.txt.
  • #10764open in new window: append to file with the >> syntax instead of overwriting with >
    (1 + 2 + 3) out> sum.txt
    "\n" out>> sum.txt
    (4 + 5 + 6) out>> sum.txt
    
    will create a file called sum.txt with the following content
    6
    15
    

One-time theming of tables is there [toc]

A common question and feature request from the community was: how can i change the theme of Nushell tables for just one command?

Because of how Nushell works, the way to do that has been the following for a long time

do {                                # use the scope of `do` to not alter the environment of the callee
    $env.config.table.mode = "none" # set the theme
    ls | table                      # call table to avoid just passing the value back to the callee and use the outside theme
}

In #11058open in new window, @zhiburtopen in new window cracked the nut and added the long-wanted table --theme option. Below is a simple example:

  • let's define a table
let table = ls | where type == dir | reject type
  • and show it with a particular theme
$table | table --theme ascii_rounded
.#--name----size-------modified------.
|0|assets |4.1 KB|2023-12-05 18:32:30|
|1|benches|4.1 KB|2023-12-09 22:33:44|
|2|crates |4.1 KB|2023-12-09 22:33:44|
|3|devdocs|4.1 KB|2023-12-09 22:33:44|
|4|docker |4.1 KB|2023-12-09 22:33:44|
|5|scripts|4.1 KB|2023-12-09 22:33:44|
|6|src    |4.1 KB|2023-12-09 22:33:44|
|7|target |4.1 KB|2023-12-09 14:19:04|
|8|tests  |4.1 KB|2023-12-05 18:32:30|
|9|wix    |4.1 KB|2023-12-05 18:32:30|
'------------------------------------'

Exposing name of script to the wild [toc]

Coming from languages such as Python and Bash, it's quite expected to be able to access the name of the script itself the same way it's been called from the CLI: with sys.argv[0] in Python and $0 in Bash.

This was not possible in Nushell until the work of @p00fopen in new window in #11203open in new window. This will expose to any script a new environment variable called $env.PROCESS_PATH and which will contain precisely something akin to argv[0]!

#!/usr/bin/env nu
# in demo.nu

def main [] {
    printf $"I was called as ($env.PROCESS_PATH)"
}
demo.nu          # will show "I was called as demo.nu"
./demo.nu        # will show "I was called as ./demo.nu"
/path/to/demo.nu # will show "I was called as /path/to/demo.nu"

Parsing human-friendly dates into Nushell values [toc]

In #11051open in new window, @fdncredopen in new window added to into datetime the ability to parse human readable dates into Nushell datetime. This allows to write crazy things like

"Now" | into datetime        # same as `date now`
"A week ago" | into datetime # same as `(date now) - 1wk`

Note

see the full list of examples with into datetime --list-human

Show found externals via syntax highlighting in the REPLopen in new window [toc]

We've added a new experimental feature that changes the syntax highlighting of text in the command position in the repl. Here's how it works. As you type, if a command is found with the current letter you've typed, the command will be highlighted according to your configuration of your color theme, specifically shape_external_resolved. As you keep typing, which fires to find out if the command is found or not. This allows you to know whether you've created a typo before you ever hit enter in the nushell repl.

Since this might have a performance impact, we've put this behind a configuration point named highlight_resolved_externals in the config.nu file. Set it to true to enable this functionality and false to disable it. It defaults to false.

Right now, with a dark theme, here's one way of configuring these colors, in the config.nu color theme, that showcases this functionality. Also, make sure you have highlight_resolved_externals set to true.

  shape_internalcall: cyan_bold               # internal commands that are found will be this color
  shape_external: darkorange                  # external "commands" that do not exist, will be this color
  shape_external_resolved: light_yellow_bold  # external commands that are found with `which`, will be this color

You can read more about it and see a gif in #11135open in new window where @fdncredopen in new window implemented the feature.

New "out of bound" error [toc]

@nibon7open in new window introduces in #11201open in new window a new error which is pretty common to programming languages: the "out of bound" error.

This will cause the following command to give an "out of bound" error:

"foo" | str index-of '' --range 10..11

Details on the 0.87.1 hotfix release [toc]

In between the last 0.87.0 release and the one from today, there has been a quick hotfix release to address two major bugs to two quite important filesystem commands: rm and cp.

Below are some details about these two bug fixes:

Need help to wrap... commands? [toc]

In the previous release, the extern-wrapped command has been deprecated and it is now being removed. An issue did persist though, making def --wrapped not a suitable replacement for the old command... which was fixed in #11235open in new window by the changes from @KAAtheWiseGitopen in new window: def --wrapped commands won't create a help page anymore, allowing to send -h and --help to other commands:

def --wrapped foo [...rest] { echo $rest }
foo --help -h

will give no help page

─┬──────
0│--help
1│-h
─┴──────

Restricting use of internal variables [toc]

Nushell features some internal variables that users can't redeclare, i.e. $nu, $env and $in.

Note

$env is the special of the three because one can mutate it with

$env.FOO = "i'm foo"

but it is impossible to do let env = 123

However, up until now and the work of @KAAtheWiseGitopen in new window in #11169open in new window and #11228open in new window, it was possible to redefined these variables in command definition: the code would parse and run, but you would get the internal values instead of the ones passed to the command, which is a nasty silent bug! From now on, the following will give an error

def foo [nu] { print $nu }
def bar [env] { print $env }
def baz [in] { print $in }

Also, because $nu and $env don't have spans by construction, i.e. they are not defined in any script where a span would make sense but rather internally when Nushell starts, accessing their metadata does not make sense. This release makes the error more helpful:

metadata $env
Error:   × Built-in variables `$env` and `$nu` have no metadata
   ╭─[entry #1:1:1]
 1 │ metadata $env
   ·          ──┬─
   ·            ╰── no metadata available
   ╰────

A small update on the LSP [toc]

@schrieveslaachopen in new window has been working again on the built-in LSP of Nushell. With #10941open in new window, completions should get better with this release :partying:

Our set of commands is evolving [toc]

As usual, new release rhymes with changes to commands!

New commands [toc]

A local in-memory database with stor

With this release we've implemented a new sqlite family of commands called stor. stor is meant to be short for "storing data in an in-memory sqlite database". That's right, this family of commands supports an in-memory database. This in-memory database will be accessible as long as they share the same executable instance. A new instance is created with each new nushell instance and the in-memory database cannot be shared between instances. We're exited to see what new things you'll invent with this technology, although we still consider it in the experimental phase. Please feel free to submit PRs to make the usage of it better and more robust. There are examples of how to use each command in the PRopen in new window.

Here the summary you get when you execute the stor command.

Usage:
  > stor

Subcommands:
  stor create - Create a table in the in-memory sqlite database
  stor delete - Delete a table or specified rows in the in-memory sqlite database
  stor export - Export the in-memory sqlite database to a sqlite database file
  stor import - Import a sqlite database file into the in-memory sqlite database
  stor insert - Insert information into a specified table in the in-memory sqlite database
  stor open - Opens the in-memory sqlite database
  stor reset - Reset the in-memory database by dropping all tables
  stor update - Update information in a specified table in the in-memory sqlite database

Flags:
  -h, --help - Display the help message for this command

Input/output types:
  ╭─#─┬──input──┬─output─╮
 0 nothing string
  ╰───┴─────────┴────────╯

Changes to existing commands [toc]

[0, 2] | update 0 {|i| $i + 1 }  # will give [1, 2]
[0, 1, 2] | insert 5 5           # will give "index too large" error

Deprecated commands [toc]

Removed commands [toc]

as part of the deprecation plan of last releaseopen in new window, the following commands and options have been removed by @amtoineopen in new window:

Breaking changes [toc]

Starting with this release, if a subexpression (eg (echo foo.txt)) or block (eg if true { echo foo.txt } evaluated to a non-zero exit code, it will no longer be lost and instead will be the exit code of that expression. This allows them to act more naturally when an external command fails.

We now error if you repeat the same field name when creating a record, for example {a: 1, a: 2}.

We've removed the deprecated commands def-env and export def-env. Instead, use def --env. Likewise, we've done the same for extern-wrapped and export extern-wrapped. Instead, use def --wrapped.

We've renamed the previous unfold command. Instead, use the generate command.

We've also removed the size command, which had an ambiguous name. Now, use str stats to get information about string content.

This release replaces the confusing --not flag of glob to instead be glob --exclude.

To make how to update the table numbering more clear, we've replaced table -n with table -i/table --index.

Full changelog [toc]

Nushell

Extension

Documentation

Nu_Scripts

Reedline