Nushell
Get Nu!
Getting Started
  • The Nushell Book
  • Command Reference
  • Cookbook
  • Language Reference Guide
  • Contributing Guide
Blog
  • English
  • 中文
  • Deutsch
  • Français
  • Español
  • 日本語
  • Português do Brasil
  • Русский язык
GitHub
Get Nu!
Getting Started
  • The Nushell Book
  • Command Reference
  • Cookbook
  • Language Reference Guide
  • Contributing Guide
Blog
  • English
  • 中文
  • Deutsch
  • Français
  • Español
  • 日本語
  • Português do Brasil
  • Русский язык
GitHub

Nushell 0.98.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.98.0 of Nu. This release changes non-zero exit codes from external commands to be handled as errors, makes the IR evaluator the default, removes support for the system clipboard, and includes many command changes and bugfixes.

Where to get it

Nu 0.98.0 is available as pre-built binaries or from crates.io. If you have Rust installed you can install it using cargo install nu.

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

  • Highlights and themes of this release
    • Non-zero exit codes are now errors
    • IR is now the default evaluator
    • Removing support for the system clipboard
    • Record parsing changes
  • Changes
    • Additions
      • metadata access
      • split cell-path
      • split column --numbered
      • encode base32, decode base32
      • encode base32hex, decode base32hex
      • encode new-base64, decode new-base64
    • Breaking changes
      • into record
      • clear
      • path exists
      • scope commands and help commands
      • tee
      • polars concatenate renamed to polars str-join
    • Deprecations
      • encode base64
    • Removals
      • str deunicode
      • $env.config.use_grid_icons
    • Bug fixes and other changes
      • find
      • encode hex
      • Various Polars commands now support expressions
      • Improved script help output
      • Bit operations and commands
      • detect columns
  • Notes for plugin developers
    • Breaking plugin changes
    • Rust API improvements
    • Other changes
  • Hall of fame
  • Full changelog

Highlights and themes of this release [toc]

Non-zero exit codes are now errors [toc]

Breaking change

See a full overview of the breaking changes

New error behavior

This release adds a new error when an external exits with a non-zero exit code. In the default config, this error is suppressed in the REPL. However, if you are upgrading with an existing config, you must add this section to your $env.config to suppress this error:

display_errors: {
    exit_code: false
    # Core dump errors are always printed, and SIGPIPE never triggers an error.
    # The setting below controls message printing for termination by all other signals.
    termination_signal: true
}

One long-requested feature for nushell is to make non-zero exit codes throw an error. Before this release, non-zero exit codes would quietly stop execution. In some cases, execution would not even stop, but rather only skip over the remaining commands in the current code block. With this release, non-zero exit codes (and termination due to unix signals) are now errors just like any other error, so nushell now runs almost as if bash's set -e option was enabled (#13515).

Errors due to non-zero exit codes can be caught in a try/catch block, and the error record passed to the catch closure will contain an exit_code column in this case.

try {
  nu -c 'exit 42'
} catch {|e|
  print $e.exit_code? # prints 42
}

As such, $env.LAST_EXIT_CODE is no longer necessary in most cases. Rather, try/catch should suffice for both internal and external errors, unifying and simplifying error handling.

Note that the complete command still works the same as it does today. If the previous command fails, its exit code and output will still be captured and returned by complete.

Some other things to note about the current implementation:

  • Only the exit code of the last command of a pipeline is checked and can cause an error. The exit codes of all the previous commands in a pipeline are ignored. In the future, we may check and track the exit codes of all commands in a pipeline to provide an equivalent to bash's set -o pipefail, but this would require a major refactor. So for now, this functionality is only a potential future addition.

  • If a command was terminated by a unix signal (e.g., SIGTERM, SIGINT), then the exit_code value in the catch error record will contain the negation of the signal number. For example, if SIGINT is signal number 2, then exit_code would be reported as -2. This is similar to how Python reports exit codes.

  • The one exception is SIGPIPE. If a command is terminated due to SIGPIPE, then it is not treated as an error.

One downside of having non-zero exit codes as errors is that uncaught errors will print a nushell error message to the terminal which may make it annoying to find any error messages printed by the external command that failed. Similarly, some commands use exit codes to indicate other things besides failure (e.g., diff). Having all these extra error message in the REPL can be annoying, so two new config options have been added as well:

  1. $env.config.display_errors.exit_code
  2. $env.config.display_errors.termination_signal

With no config (e.g., when running a script), these config options are both true and the corresponding error cases will be printed as a nushell error message. However, the default config (i.e., config nu --default) sets display_errors.exit_code to false. So, if you use the default config, or set this option in your config, then an error message won't be printed for non-zero exit codes in the REPL.

One last thing to note is that core dumps are treated as a separate error case and an error message will always be printed if the error is uncaught (i.e., there is no config option for this).

IR is now the default evaluator [toc]

Nushell 0.96.0 introduced internal representation (IR), with a new compiler and evaluator to take some of the work away from runtime, enable future optimizations, and create a well-known target for our ongoing parser replacement work.

The IR evaluator has now been battle-tested by many Nushell contributors and power users and passes our entire test suite, so we've decided to make it the default now and hopefully remove the old evaluator in the next release if all goes well.

This means that the NU_USE_IR environment variable is no longer respected, and instead we've added NU_DISABLE_IR which will go back to the old evaluator if present. If you find that you need to disable IR to make code that was previously working function, please file a bug report so that we can fix it before the next release.

This may change the debug profile switches you need to use for useful output - expressions are not a valid mode for IR, you need to debug instructions instead.

Removing support for the system clipboard [toc]

Breaking change

See a full overview of the breaking changes

With #13694, support for the system clipboard in keybinds has been removed, as this is not something that we think we can provide good, cross-platform support for in a secure manner. Namely,

  • Tests for the clipboard feature are lacking due to the difficulty in setting up tests on each platform.
  • The clipboard feature is known to be problematic on certain systems, potentially affecting startup times. Due to the lack of tests, other unfound issues may be lurking.
  • The system clipboard adds a potential attack surface.
  • The clipboard feature adds 1MB to the binary size.

So, going forward, users that want the system clipboard functionality will have to compile a custom build of nushell by enabling the system-clipboard cargo feature. As an alternative, we recommend users delegate system clipboard handling to their terminal.

Record parsing changes [toc]

Breaking change

See a full overview of the breaking changes

Previously, it was impossible to use datetime literals with colons in record literals without adding parentheses. But thanks to @cyradotpink in #13413, this is now possible.

# Previously, parentheses were necessary:
{ foo: (2024-08-13T22:11:09) }

# But this now works!
{ foo: 2024-08-13T22:11:09 }

To make this possible, a few changes were made to record parsing. This should not affect most code, but some previously valid code no longer parses successfully. For example:

{ :: 1 }
# This used to parse as { ':': 1 }, but it is now an error.

Changes [toc]

Additions [toc]

metadata access [toc]

Thanks to @Bahex in #13785, a new metadata access command was added. It takes a closure argument, and the first argument to the closure will be the metadata record. Additionally, the pipeline input to metadata access will be forwarded to the closure as is. So, unlike the metadata command, this new command allows you to get the metadata of a pipeline without consuming the pipeline.

split cell-path [toc]

In #13705, @Bahex also added the split cell-path command. It is essentially the opposite of into cell-path and returns a list of the path members of the input cell path.

split column --numbered [toc]

Thanks to @nome in #13831, a --numbered flag was added to split column which limits the number of times to split the input string (just like split row --numbered).

encode base32, decode base32 [toc]

In #13428, @KAAtheWiseGit added this common variant of Base32 which uses letters A-Z followed by t he digits 2-7, specifically chosen to avoid letters and digits that look similar (RFC 4648 section 6).

encode base32hex, decode base32hex [toc]

In #13428, @KAAtheWiseGit added this variant of Base32 which only uses the 22 letters past 0-9 sequentially (RFC 4648 section 7).

encode new-base64, decode new-base64 [toc]

In #13428, @KAAtheWiseGit added the encode new-base64 and decode new-base64 commands, which will eventually be renamed back to encode base64 and decode base64. These commands are more standards-conformant and rather than taking a custom alphabet option, they support --url for the URL-safe base64 variant, and --nopad to handle Base64 without padding.

Breaking changes [toc]

into record [toc]

In #13637, several changes/improvements were made to list inputs for into record:

  • A list of pairs of values will be converted into a record. For example:
    [[a 1] [b 2]] | into record
    # { a: 1, b: 2 }
  • A list of records are merged into a single record (records at the end of the list have precedence):
    [{ a: 1 } { b: 2 } { a: 3 }] | into record
    # { a: 3, b: 2 }

As a consequence, the previous behavior for list inputs has been removed (the index of each item would be used as the key). Instead, this old behavior can now be accomplished through either of the following:

0.. | zip $list | into record

$list | enumerate | transpose -r -d | into record

Additionally, into record no longer supports range values as input.

After #13650, into record also now outputs millisecond, microsecond, and nanosecond columns when provided with a datetime value.

clear [toc]

In #13821, @T3sT3ro changed the clear command to clear the entire scrollback buffer. As such, the --all flag was removed, since it is now the default behavior. Instead, a --keep-scrollback flag was added which, when used, makes clear clear only the terminal (the previous default behavior).

path exists [toc]

After #13763, if an I/O error occurs when checking if a path exists, false is now returned instead of throwing an error (this includes errors due to insufficient permissions).

scope commands and help commands [toc]

With #13598, two output columns have been renamed for scope commands and help commands:

  1. usage has been renamed to description
  2. extra_usage is now extra_description

tee [toc]

tee now works as expected for non-collection input (#13652), and will no longer implicitly handle the input as if it were a singleton list. This does create some hairy situations for error handling, namely:

  • For string or binary input, the input will be converted to a stream (even if it wasn't one), and the closure provided to tee must finish in order for the output of tee to end. If an error is produced by the closure, it will be forwarded to the output stream.
  • For any other non-collection input, any error in the tee closure will be logged to standard error, but there isn't really anything else we can do because we can't wait for that closure to finish.

polars concatenate renamed to polars str-join [toc]

In #13781, polars concatenate was renamed to polars str-join to match the name of the Nushell str join command. It also now has support for expressions.

Deprecations [toc]

encode base64, decode base64 [toc]

In #13428, @KAAtheWiseGit added encode new-base64 / decode new-base64 to replace these.

Removals [toc]

str deunicode [toc]

In #13693, the str deunicode command (added in v0.96.0) has been removed due to concerns with stability and support post v1.0.

$env.config.use_grid_icons [toc]

After #13788, the use_grid_icons config option has been removed. Instead, the grid command now has an --icons flag that has the same effect.

Bug fixes and other changes [toc]

find [toc]

After #13848, the find command no longer strips tabs from its input.

Also, regex patterns are now properly escaped after #13792.

encode hex [toc]

In #13428, @KAAtheWiseGit added the --lower flag to encode hex, to produce lowercase hex.

Various Polars commands now support expressions [toc]

In #13724, #13726, and #13799, these Polars plugin commands were updated to support expressions:

  • polars uppercase
  • polars lowercase
  • polars replace
  • polars replace-all
  • polars cumulative

Improved script help output [toc]

Thanks to @gwenya in #13445, the help output for scripts now uses the name of the script instead of main. The available subcommands are now also listed in the help output.

Bit operations and commands [toc]

bits ror and bits rol had undefined behavior in some cases and could also panic in others. This has been fixed in #13673. Similarly, bugs for bits shl, bits shr, bit-shl, and bit-shr were fixed in #13663.

detect columns [toc]

One source of panics was fixed in #13752.

Notes for plugin developers [toc]

Breaking plugin changes [toc]

  • usage in signatures has been changed to description (#13598). This affects both the Rust API and the plugin protocol.

Rust API improvements [toc]

  • The IntoValue and FromValue traits now work well with Vec<u8> byte arrays and bytes::Bytes (#13641), converting to binary and from string/binary.
  • IntoValue is now supported for several Nushell types, as well as HashMap, DateTime, and &str (#13744)
  • The derive macros for IntoValue and FromValue now also take #[nu_value(type_name = "...")] to set a type name for the expected type. (#13647)

Other changes [toc]

  • The nu-plugin library now uses MessagePack instead of bincode internally for custom value serialization. This is not a protocol change as the representation of custom values is intentionally not defined, and they're treated as a black box by the engine. (#13745)

All breaking changes [toc]

  • #13821 Improve #12008 UX, clear scrollback by default on clear
  • #13763 fix path exists on a non-directory file
  • #13515 Error on non-zero exit statuses
  • #13788 remove config use_grid_icons, move to parameter of grid command
  • #13781 Renamed polars concatenate and added expression support.
  • #13652 Make tee work more nicely with non-collections
  • #13745 Remove bincode and use MessagePack instead for plugin custom values
  • #13694 Remove system-clipboard from the default build
  • #13693 Remove str deunicode
  • #13428 encode/decode for multiple alphabets
  • #13598 Change the usage misnomer to "description"
  • #13637 Change behavior of into record on lists to be more useful

Hall of fame [toc]

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

authortitlePR
@fdncredallow tab to be retained with find#13848
@zhiburtFix padding issue with header_on_border#13808
@IanManskeFix IR for try#13811
@ayax79String values should pass their content-type correctly on http requests with a body (e.g. http post)#13731
@devynRemove bincode and use MessagePack instead for plugin custom values#13745
@ysthakurUse right options in custom completions#13698
@KAAtheWiseGitFix encode/decode todo's#13683
@sholderbachFix bits ror/bits rol implementation#13673
@sholderbachFix bugs and UB in bit shifting ops#13663

Full changelog [toc]

  • ayax79 created
    • Improved null handling when converting from nu -> dataframe.
    • catch unwrap on panics with polars collect
    • Make polars save return an empty pipeline
    • Migrating polars commands away from macros, removed custom DataFrame comparison.
    • Added documentation explanation explaining how to select all columns with polars col
    • Added expression support for polars cumulative
    • Polars command reorg
    • String values should pass their content-type correctly on http requests with a body (e.g. http post)
    • Use String::contains instead of exact match when matching content types for http requests.
    • Renamed polars concatenate and added expression support.
    • Added expression support for polars str-lengths
    • Expression support for polars str-slice
    • Added expression support for polars contains
    • Expression support for polars strftime
    • Expression support polars replace and polars replace-all
    • Expression support for polars uppercase and polars lowercase
    • Added polars commands for converting string columns to integer and decimal columns
    • Changed category for panic and added search terms and examples
  • devyn created
    • Make IR the default evaluator
    • Fix try: Add set_last_error() to prepare_error_handler() for IR eval
    • Fix remaining mismatch for env handling in IR
    • Make tee work more nicely with non-collections
    • Remove bincode and use MessagePack instead for plugin custom values
    • Change behavior of into record on lists to be more useful
  • hustcer created
    • Fix dockerfile and reset Nu config to default
    • Update Nu for release workflow and fix build warning of musl targets
    • Add aarch64-unknown-linux-musl and armv7-unknown-linux-musleabihf targets to release workflow
    • Try to add aarch64-unknown-linux-musl and armv7-unknown-linux-musleabihf release target
  • fdncred created
    • allow tab to be retained with find
    • bump rust version to 1.79.0
    • update the latest reedline
    • remove config use_grid_icons, move to parameter of grid command
    • fixed issue with find not working with symbols that should be escaped
    • fix --ide-ast when there are errors
    • add version and path to plugin executable help
    • remove cfg_atter tarpaulin
    • update virtual terminal processing
    • add more granularity for into record with dates
  • nome created
    • Add --number flag to split column
  • T3sT3ro created
    • Improve #12008 UX, clear scrollback by default on clear
  • WindSoilder created
    • fix path exists on a non-directory file
    • Config: change ctrl-k to cuttolineend event
    • make a user friendly message when use_grid_icons is used in config
    • Don't panic on detect columns with --guess flag
  • Bahex created
    • add metadata access command
    • Add split cell-path
  • zhiburt created
    • Fix padding issue with header_on_border
    • Check fix for emojie, wrap issues
  • app/dependabot created
    • Bump crate-ci/typos from 1.24.4 to 1.24.5
    • Bump hustcer/setup-nu from 3.12 to 3.13
    • Bump shadow-rs from 0.33.0 to 0.34.0
    • Bump crate-ci/typos from 1.24.1 to 1.24.4
    • Bump nix from 0.28.0 to 0.29.0
    • Bump itertools from 0.12.1 to 0.13.0
    • Bump indexmap from 2.4.0 to 2.5.0
    • Bump shadow-rs from 0.31.1 to 0.33.0
    • Bump crate-ci/typos from 1.23.6 to 1.24.1
  • IanManske created
    • Fix IR for try
    • Error on non-zero exit statuses
    • Use IntoValue in config code
    • Implement IntoValue for more types
    • Be explicit about reduce args and input
  • Ancient77 created
    • Expand multiple dots in path in completions
  • JTopanotti created
    • Refining error handling in http post
    • Refactor send_request in client.rs
  • sholderbach created
    • Fixup serde feature selection in nu-protocol
    • Remove unneeded serde feature on byte-unit dep
    • Setup global cargo lint configuration
    • Remove system-clipboard from the default build
    • Remove str deunicode
    • Simplify our bug reporting form
    • Remove unnecessary sort in explore search fn
    • Fix bits ror/bits rol implementation
    • Change the usage misnomer to "description"
    • Fix bugs and UB in bit shifting ops
    • Bump version to 0.97.2
  • cptpiepmatz created
    • Add #[nu_value(rename = "...")] as helper attribute on members for derive macros
    • Use heck instead of convert_case for nu-derive-value
    • Added record key renaming for derive macros IntoValue and FromValue
    • Change expected type for derived FromValue implementations via attribute
    • Improve working with IntoValue and FromValue for byte collections
  • cyradotpink created
    • Fix parsing record values containing colons
  • weirdan created
    • Respect user-defined $env.NU_LOG_FORMAT and $env.NU_LOG_DATE_FORMAT
  • ysthakur created
    • Use right options in custom completions
  • KAAtheWiseGit created
    • Fix encode/decode todo's
    • Remove unused same-file workspace dependency
    • encode/decode for multiple alphabets
  • poliorcetics created
    • doc: fix broken doc links
  • gwenya created
    • Improve help output for scripts
Edit this page on GitHub
Contributors: Ian Manske, Devyn Cairns, 132ikl, fdncred