Nushell 0.77

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.77 of Nu. This release adds reworked aliases, more consistent timestamp handling, reworked XML support, and more.

Where to get it

Nu 0.77 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

Reworked aliases (Breaking changes!) (kubouchopen in new window)

Aliases have been a constant source of panics and growing code complexity as a result of trying to patch the panics. In this release, we re-implement aliases from scratch. Instead of replacing spans of expressions, aliases are implemented as another type of command, quite like extern is used to implement known externals. Alias is a command that wraps another command call. As a result, in some cases, aliases do not behave exactly the same as before. Here are the key facts:

  • Alias can only alias another command call. For example, alias la = ls -a works, but the following does not:
    • alias foo = "foo"
      • "foo" is not a command call, use alias foo = echo "foo" instead
    • alias lsn = (ls | sort-by type name -i)
      • subexpression is not a command call, use a custom command instead
  • Alias cannot alias command named the same as the alias. E.g., alias ls = ls -a is not possible currently, and gives an unhelpful error message. We plan to fix this as soon as possible and in the future we aim for this to work.
  • Some parser keywords are not allowed to be aliased. Currently, overlay commands can be aliased but the other parser keywords can not. We can add support for aliasing more parser keywords in the future.

If some of the above is too limiting for you, the old aliases are still unchanged and available as old-alias. Just change alias to old-alias and it should work the same as before. If there are no more problems with the new alias implementation, and we manage to iron out the recursive alias issue, we will remove old-alias in the next release, otherwise, we'll keep it around longer.

More consistent timestamp handling (bobhyopen in new window)

Simplified conversion between Nushell date type and unix timestamps (#8244open in new window).

Nushell now standardizes on representing a Unix timestamp as a number of nanoseconds relative to the unix epoch 1970-01-01 00:00:00 +0000 (UTC). Since the timestamp is stored in a (64 bit signed) Nushell int type, this limits the range of dates that can be represented to approximately 21-sep-1677 through 11-apr-2262.

In prior versions, Nushell attempted to extend the range of representable dates by allowing multiple resolutions of timestamps (seconds, milliseconds as well as nanoseconds) to be stored and relied on arbitrary range check heuristics to disambiguate the value intended. However, there were bugs in the checks and incorrect results could be produced.

With this change <int> | into datetime assumes the input is a number of nanoseconds and can never produce a date outside this range:

〉"7fffffffffffffff" | into int -r 16 | into datetime
Fri, 11 Apr 2262 23:47:16 +0000 (in 239 years)
("7fffffffffffffff" | into int -r 16) * -1 | into datetime
Tue, 21 Sep 1677 00:12:43 +0000 (345 years ago)

The timestamp epoch is the standard unix epoch. Note the timezone is UTC/GMT:

〉0 | into datetime
Thu, 01 Jan 1970 00:00:00 +0000 (53 years ago)

<datetime> | into int can now produce an error if the input is outside the supported range:

〉1492-10-12 | into int
Error: nu::shell::incorrect_value

  × Incorrect value.
   ╭─[entry #51:1:1]
 1 1492-10-12 | into int
   ·              ────┬───
   ·                  ╰── DateTime out of timestamp range 1677-09-21T00:12:43 and 2262-04-11T23:47:16
   ╰────

And finally, although not strictly required by the above fix, <date> | date to-record and <date> | date to-table now have a nanosecond field containing the subsecond residue of the input value (however it was produced).

〉"7fffffffffffffff" | into int -r 16 | into datetime | date to-record
╭────────────┬───────────╮
 year 2262
 month 4
 day 11
 hour 23
 minute 47
 second 16
 nanosecond 854775807
 timezone +00:00
╰────────────┴───────────╯
〉"7fffffffffffffff" | into int -r 16 | into datetime | date to-table
╭───┬──────┬───────┬─────┬──────┬────────┬────────┬────────────┬──────────╮
 # │ year │ month │ day │ hour │ minute │ second │ nanosecond │ timezone │
├───┼──────┼───────┼─────┼──────┼────────┼────────┼────────────┼──────────┤
 0 2262     4  11   23     47     16  854775807 +00:00
╰───┴──────┴───────┴─────┴──────┴────────┴────────┴────────────┴──────────╯

New XML format (NotLebedevopen in new window)

New format for xml data created and accepted by from xml and to xml commands (#7947open in new window).

Commands from xml and to xml now use format where each xml entry is represented by a single {tag: <tag name> attributes: <tag attributes> content: [<child entries>]} record. Special xml entries also use this record, replacing irrelevant fields with null for easier use.

Reading some simple xml:

〉'<release>
    <project repo="https://github.com/nushell/nushell">nushell</project>
    <version>0.77</version>
    <message>Now with better xml!</message>
  </release>' | from xml
╭────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
 tag release
 attributes {record 0 fields}                                                                                           
 ╭───┬─────────┬───────────────────────────────────────────────┬───────────────────────────────────────────╮
 content # │   tag   │                  attributes                   │                  content                  │ │
 ├───┼─────────┼───────────────────────────────────────────────┼───────────────────────────────────────────┤
 0 project ╭──────┬────────────────────────────────────╮ ╭───┬─────┬────────────┬─────────╮
 repo https://github.com/nushell/nushell # │ tag │ attributes │ content │        │ │
 ╰──────┴────────────────────────────────────╯ ├───┼─────┼────────────┼─────────┤
 0 nushell
 ╰───┴─────┴────────────┴─────────╯
 1 version {record 0 fields}                              ╭───┬─────┬────────────┬─────────╮
 # │ tag │ attributes │ content │        │ │
 ├───┼─────┼────────────┼─────────┤
 0 0.77
 ╰───┴─────┴────────────┴─────────╯
 2 message {record 0 fields}                              ╭───┬─────┬────────────┬────────────────╮
 # │ tag │ attributes │    content     │ │ │
 ├───┼─────┼────────────┼────────────────┤
 0 Now with
 better xml!
 ╰───┴─────┴────────────┴────────────────╯
 ╰───┴─────────┴───────────────────────────────────────────────┴───────────────────────────────────────────╯
╰────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

Creating a little html page. In case of to xml one can deviate from rigid structure and omit empty fields of records:

〉{tag: html content: [
  {tag: body content: [
    {tag: h1 content: ['Hello from Nushell !']}
    {tag: a attributes: {href: 'https://www.nushell.sh/'} content: ['Learn more here']}
    {tag: p content: [$"Current time is (date now)"]}
  ]}
  ]} | to xml
<html><body><h1>Hello from Nushell !</h1><a href="https://www.nushell.sh/">Learn more here</a><p>Current time is Mon, 13 Mar 2023 21:20:56 +0300 (now)</p></body></html>

New additions to $nu (StevenDoesStuffsopen in new window, amtoineopen in new window)

The builtin $nu variable now contains new entries:

  • is-interactive: Nushell was launched in interactive mode
  • is-login: Nushell was launched in login mode
  • startup_time: Nushell's startup time

Reworked http subcommands (jaudigeropen in new window, jaudigeropen in new window)

The http command now has more subcommands and existing subcommands have been reworked:

> help http
Various commands for working with http methods.

You must use one of the following subcommands. Using this command as-is will only produce this help message.

Search terms: network, fetch, pull, request, download, curl, wget

Usage:
  > http

Subcommands:
  http delete - Delete the specified resource.
  http get - Fetch the contents from a URL.
  http head - Get the headers from a URL.
  http patch - Patch a body to a URL.
  http post - Post a body to a URL.
  http put - Put a body to a URL.

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

Signatures:
  <nothing> | http -> <string>

Make sure to browse the help messages of these commands. They contain fully functional examples thanks to pointing at www.example.com.

Examples results are now shown in help pages (amtoineopen in new window)

When commands define expected results for their examples, the output is now shown just below the associated examples.

Let's take the merge command as an example!

Before the change, the output of help merge would give

Examples:
  Add an 'index' column to the input table
  > [a b c] | wrap name | merge ( [1 2 3] | wrap index )

  Merge two records
  > {a: 1, b: 2} | merge {c: 3}

  Merge two tables, overwriting overlapping columns
  > [{columnA: A0 columnB: B0}] | merge [{columnA: 'A0*'}]

Now, it gives the output of all the example commands:

Examples:
  Add an 'index' column to the input table
  > [a b c] | wrap name | merge ( [1 2 3] | wrap index )
  ╭───┬──────╮
 # │ name │
  ├───┼──────┤
 1 a
 2 b
 3 c
  ╰───┴──────╯

  Merge two records
  > {a: 1, b: 2} | merge {c: 3}
  ╭───┬───╮
 a 1
 b 2
 c 3
  ╰───┴───╯

  Merge two tables, overwriting overlapping columns
  > [{columnA: A0 columnB: B0}] | merge [{columnA: 'A0*'}]
  ╭───┬─────────┬─────────╮
 # │ columnA │ columnB │
  ├───┼─────────┼─────────┤
 0 A0* B0
  ╰───┴─────────┴─────────╯

💡 Note the website has been modified accordingly with the same outputs.

Breaking changes

  • Alias changes, see above
  • Comments on the same line as code now require a space before # to be recognized as such. This allows you to have a string foo#bar with a pound sign. (#8151open in new window)
  • env command has been removed, use $env instead (https://github.com/nushell/nushell/pull/8185)
  • str trim no longer has --all, --both, and --format flags. str replace should be an adequate replacement; please let us know if it is not (#8205open in new window)
  • The changes to timestamp handling noted above (#8244open in new window) can require code changes to existing scripts:
    • Saved data containing the results of an old datetime-to-timestamp conversion will not deserialize correctly when read back by the current version of Nushell. In general, Nushell will produce incorrect datetime values without noting an error.
    • <int> | into datetime now assumes nanosecond scaling for all timestamps. You must ensure all timestamps computed by your script or retrieved from external sources are scaled appropriately.
    • <date> | into int can now fail, as noted above. You cannot rely on this operation to persist a arbitrary date.
  • The change to from xml and to xml commands noted above (#7947open in new window) will require to update scripts relying on old output/input format.
  • mkdir, cp, mv and rm return nothing. Errors and actions with --verbose flag are printed to stderr instead (#8014open in new window).
  • Plugin authors relying on nu_protocol::Value may need to update their code to account for a change to Value::Error (#8375open in new window)
  • Different types of lists can now be appended. This can break scripts that were relying on the stricter behavior (https://github.com/nushell/nushell/pull/8157)

Full changelog

Extensions

Documentation

nu_scripts

reedline