Nushell 0.68

Nushell, or Nu for short, is a new shell that takes a modern, structured approach to your commandline. 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 commandline pipelines.

Today, we're releasing version 0.68 of Nu. This is release a rework of modules, a new source-env command, overlay changes, and more.

Where to get it

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

If you want all the built-in goodies, you can install cargo install nu --features=extra.

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

(Major changes!) Rework of modules and environment

Some of the changes here have far-reaching consequences and it might take a while to get the book up to speed.

This release includes a bundle of changes to environment handling and modules. For now, we kept also the old functionality, but in the 0.69, it will be removed. It is therefore recommended to port your scripts and modules to the new style to avoid breakages in the next release. You can read a more complete write-up on the motivation and overall design in this documentopen in new window.

source becomes source-env (JT, kubouchopen in new window)

Note: Since the release we found out that source-env with a dynamic path is not viable and had to make it require a constant string or path, just like source. The command still works as described below but as of 0.68.1, source-env requires a static path as an argument. This is not where our design was supposed to land and we'll be searching for alternatives. We might also postpone the deprecation of the existing module commands beyond 0.69. Thanks for understanding.

One of the most common pitfalls of Nushell was trying to source a dynamic path, such as source ($env.PWD | path join Since Nushell is a "compiled" language where commands and aliases are analyzed while parsing ("compiling") the code, sourcing dynamic paths is not possible for the same reason you cannot dynamically set #include file names in C or use modules in Rust. You can read a bit more about this in our Thinking in Nu book chapteropen in new window.

To address this pitfall, we decided to change source to source-env which can be used to bring in the environment, but not custom commands, aliases and variables anymore. A benefit of doing so is that it is now possible to pass dynamic paths: source-env ($env.PWD | path join would bring in the environment from the file.

How do you bring in commands and aliases without source? You need to use a module and the use keyword. See our book chapter about modulesopen in new window, it's quite simple. Alternatively, you can use overlaysopen in new window.

How do you bring in variables without source? This is not possible anymore. A workaround is to define a command in your module that will return the value you want.

source still continues to work in this release but will be removed in 0.69. In 0.69, we will also change all config files to be modules, not plain scripts.

Module envrionment changes (kubouchopen in new window, kubouchopen in new window)

The way to define environment variables from modules used to be

> module spam {
    export env FOO { 'bar' }

> use spam

This example shows one problem: it is easy to end up with namespaced environment variables, which in this case would be $env.'spam FOO'. Another problem with the current design is that use is a parser keyword (like the removed source) but contains both parser ("compiled") and runtime (evaluated) functionality. Since 0.67, it is possible to use modules within other modulesopen in new window but because environment is 100% handled in runtime, and use never evaluates the module itself, it was impossible to bring in environment variables from other modules inside a module.

Long story short, use (and hide) now handle only custom commands and aliases, not environment variables anymore. If you want to bring both environment and commands/aliases, you need to use the source-env and use commands separately (or use overlays).

Also, we simplified defining the environment in modules. Instead of defining environment variables with export env individually, there is a single export-env { } block for the whole module (see the example in the next section).

If you call source-env on that module, the export-env command will get evaluated and its environment kept in the current scope.


export-env {
    let-env FOO = 'foo'
    let-env BAR = 'bar'
> source-env

> $env.FOO

> $env.BAR

This release makes export env deprecated and it will be removed in 0.69.

Syntax unification (kubouchopen in new window)

Previously, modules had some reserved syntax that was not valid in scripts: the export keywords. In this release, we allowed export keywords to be used in scripts (they do nothing: export def acts as def etc.) and thus script syntax is a superset of module syntax.

Modules can now be evaluated. This is the reason the above example with source-env works: Thanks to the unified syntax, source-env will evaluate the module which evaluates the export-env command inside the module.

Another nice thing about the unified syntax is that commands like nu-highlight now do not break or do not need to rely on heuristics if they are asked to parse a module code. Any module code is a valid script code.

Overlay changes (kubouchopen in new window, WindSoilderopen in new window)

overlay add and overlay remove are now renamed to overlay use and overlay hide (see the breaking changes later).

The functionality of these commands remains largely the same with one change being that overlay use will now evaluate the export-env { } block. Consider this module:


export-env {
    load-env {
        FOO: 'foo'
        BAR: 'bar'

export def foo [] { 'foo' }
export alias bar = 'bar'

instead of:

> source-env

> use *

you can do just:

> overlay use

You can think of overlay use as calling source-env and use in one command and putting the result into a new overlay.


Here is a table that summarizes the changes:

commandprevious release (0.67)this release (0.68)next release (0.69)
sourceimports everything into the current scopesame (deprecated)removed
source-envN/Aimports environment variablessame
useimports environment variables, commands and aliasessameimports only commands and aliases
hidehides enviornment variables, commands and aliasessamehides only commands and aliases
hide-envhides environment variablessamesame
export envdefines a single environment variable in a modulesameremoved
export-envN/A(in a module) defines the envrionment for the whole modulesame
export-envN/A(in a script) when evaluated, preserves the environment from the blocksame
export ...only allowed in a moduleallowed in a script as wellsame
config.nuplain scriptplain scriptmodule
env.nuplain scriptplain scriptmodule
login.nuplain scriptplain scriptmodule

Allow parentheses around command signatures (JTopen in new window)

To bring more familiarity with other languages, we added the option to define command signatures with parentheses () instad of only braces []:

def foo (x: int) { $x + 100 }

The square braces [] continue to work as well. This change is intended to test it with a larger audience to decide which one we prefer the most.

We added a new command str distance which implements the Levenshtein alorithm fdncredopen in new window

This example shows that the edit distance is one edit step difference using the Levenshtein algorithm.

> 'nushell' | str distance 'nutshell'
│ distance │ 1 │

We'd eventually like to add more similarity comparision functionality to nushell.

We added string duration conversion to named durations fdncredopen in new window

The new parameter on into duration --convert allows you to convert from string durations into named durations.

> '7min' | into duration --convert sec
420 sec

External Completions (experimental) (herlon214, rsteubeopen in new window)

In this release, we're trying out integrating Nushell with external completers, instead of relying solely on Nushell ones. It is possible to set the external_completer field in a config to be a block which will be evaluated if no Nushell completions were found. You can configure the block to run an external completer, such as carapaceopen in new window.

This example should enable carapace external completions:

let carapace_completer = {|spans|
    carapace $spans.0 nushell $spans | from json

# The default config record. This is where much of your global configuration is setup.
let-env config = {
    # ... your config
    external_completer: $carapace_completer

Note that this functionality is not perfectly polished yet and in some cases the external completer is not triggered correctly (see this issueopen in new window).

It is also possible to extend the parameters passed to the completer block that are required for other tools than carapace, such as cursor position etc. In theory, this feature could allow you to utilize any existing completions library from any shell, such as bash, as long as you can somehow get a list of completions from them.

Breaking changes

Renaming of all? to all, any? to any, and empty? to is-empty (adamijakopen in new window)

The ? suffix on the three commands all?, any?, empty? did not indicate a specific meaning across other commands. Other commands returning a boolean value, like str contains for example, don't carry the suffix. To remove a potential source of confusion and to free up the ? for potential use in a more meaningful semantic context, we decided to remove the suffix and rename empty? to is-empty to clarify its role.

Please update your scripts accordingly:

old namenew name

Renaming overlay commands (WindSoilderopen in new window)

old namenew name
overlay addoverlay use
overlay removeoverlay hide

The main reason is that the overlay remove does not really remove the overlay. It deactivates it and it can be resumed again from where you left off. Therefore, we felt like hide is a better word to match this functionality and aligns with our existing use and hide keywords.

path split behaviour for Windows paths (merelymyselfopen in new window)

path split no longer returns drive letters and the root directory as seperate elements for absolute Windows paths.

Previously, `C:\temp` | path split returned C:, \, and temp. Now it returns C:\ and temp.

Next Steps

We've been progressing with our design towards 0.80 as outlined in this Notion pageopen in new window.

Some time was spent trying out possible new syntax directions but we were not confident to release them yet. In the next release we'll see a removal of features deprecated in this release and we'll continue to push ahead for the 0.80.

Full changelog



Nu Scripts