Skip to main content

Nushell next generation shell

· 8 min read
Christian Rieger
The Architect of Innovation & Automation

Since the dawn of time, linux/unix shells are mostly posix compatible. While being widely used, posix compatible shells share the same limitations and bad design decisions. Posix shell scripting can lead to unexpected errors and systemfailures if not executed correctly.

In computer science focused fields, shell scripts decelerate every implementation that relies on operating system level automatization. This is especially the case for ops and devops applications like CICD (Continuous Integration with Continuous Delivery/Deployment) where it is not viable to deploy a runtime / toolchain for a more high-level typesafe / nullsafe language. For ops and devops we need good tooling in order ensure safe and fault tolerant operations. We, at RiKuWe try to reduce the surface of potential errors and mishaps as much as possible, which is why we always search for better and more reliable technologies to improve the quality of our services - for our own and our customers benefit. In this article I will introduce a shell called nushell which provides a great alternative to posix compatible shells for ops and devops environments.

What is nushell?

Nushell is a shell written in Rust. Even though the nu scripting language is heavily inspired by rust it contains similar functionality found in other shells, like accessing of environment variables in powershell, or the unix style pipes for data processing. In contrast to other shells nushell is type and object aware. The nu scripting language enables type annotations for commands (functions) and variables. If an assignment violates the target type, nushell reports an error. Unlike rust, nushell type-checks happen at runtime, but the nushell built-in language server reports type violations during programming.

Nushell types

  • records: object likes types
  • tables: rows of data, each with the same structure and types
  • lists: an arbitrary amount of items which can be extended or modified
  • directory: a valid path to a directory
  • path: a valid path to a file
  • strings: normal string, output on non nushell known applications is interpreted as a string
  • and many more...

Nushell example

Simple Pipeline: This example is only using nushell built-in functions. In nushell the built-in ls command provides a nu table with the directory contents. We can use nushell to filter and modify the table provided by ls.

ls produces the following table

╭────┬───────┬─────────┬────────┬───────────────╮
│  # │ name  │  type   │  size  │   modified    │
├────┼───────┼─────────┼────────┼───────────────┤
│  0 │ ISO   │ dir62 B │ 8 months ago  │
│  1 │ bin   │ symlink │    7 B │ 4 months ago  │
│  2 │ boot  │ dir4.0 kB │ 55 years ago  │
│  3 │ cache │ dir22 B │ 3 months ago  │
│  4 │ dev   │ dir4.1 kB │ 21 hours ago  │
│  5 │ etc   │ dir4.1 kB │ 2 hours ago   │
│  6 │ home  │ dir10 B │ 9 months ago  │
│  7 │ lib   │ symlink │    7 B │ 4 months ago  │
│  8 │ lib64 │ symlink │    7 B │ 4 months ago  │
│  9 │ mnt   │ dir38 B │ 2 months ago  │
│ 10 │ opt   │ dir154 B │ 2 months ago  │
│ 11 │ proc  │ dir0 B │ a day ago     │
│ 12 │ root  │ dir206 B │ 3 weeks ago   │
│ 13 │ run   │ dir880 B │ 21 hours ago  │
│ 14 │ sbin  │ symlink │    7 B │ 4 months ago  │
│ 15 │ srv   │ dir14 B │ 9 months ago  │
│ 16 │ sys   │ dir0 B │ a day ago     │
│ 17 │ tmp   │ dir940 B │ 2 minutes ago │
│ 18 │ usr   │ dir112 B │ 2 hours ago   │
│ 19 │ var   │ dir126 B │ a day ago     │
│ 20 │ vms   │ dir52 B │ 2 weeks ago   │
╰────┴───────┴─────────┴────────┴───────────────╯

The nu pipeline

Commands like those, where results are processed using pipes are called nu pipelines. This nu pipeline lists the contents of the root directory of my filesystem (I use arch, btw), removes all directories and entries which are smaller than 1KB, sorts the result by their size, selects only the name, type and size columns and converts the resulting table into a markdown table. sorts the result by their size, selects only the name, type and size columns and converts the resulting table into a markdown table. This blog entry is written in markdown, so the markdown table is properly rendered. Take a look at the 1KB in the example: Because size is from the type filesize the value to compare it with also needs to be a valid file size.

ls / | where type == dir and size > 1KB | sort-by size | select name type size | to md --pretty

Resulting markdown table

nametypesize
/bootdir4.0 kB
/devdir4.1 kB
/etcdir4.1 kB

Scripting examples Nushell vs Bash

In the following examples I will use the cat command because its error message depicts the provided arguments well. Also cat is a system binray which shows that nushell also interacts well with non-nushell executables.

Bash

export SOME_ENV_VAR="some string with spaces"
# this results in 4 arguments instead of one
echo $SOME_ENV_VAR
# when used with cat: sh-5.2$ cat $SOME_ENV_VAR
output:
cat: some: No such file or directory
cat: string: No such file or directory
cat: with: No such file or directory
cat: spaces: No such file or directory

The solution in bash is to surround the argument with "" e.g. "${SOME_ENV_VAR}" This is also the solution if the environment variable is not set, but in this case an empty environment variable results in an empty string which hides the error.

So the 2 options we have in bash are:

  • strange behaviour because the shell interprets values as individual arguments
  • strange behaviour because the shell ignores unset variables completely
  • hidden errors due to defaulting to empty string if used with ""

More experienced shell users know to set the -eu flag at the beginning of a shell script, but not all shells support this option and in devops environments we are confronted with different shells because of all the different docker containers which are used to build and deploy our products.

Nushell

Nushell is aware of variables, arguments and types. Instead of interpreting every word as argument nushell recognizes, that there is only one argument for cat.

$env.SOME_ENV_VAR_WITH_SPACES = "some string with spaces"
cat $env.SOME_ENV_VAR_WITH_SPACES
output:
cat: 'some string with spaces': No such file or directory

Nushell is also able to use one variable to be expanded into multiple arguments. This can be achieved using the spread operator: ...

$env.MULTIPLE_ARGUMENTS = ["first", "second", "third"]
cat ...($env.MULTIPLE_ARGUMENTS)
output:
cat: first: No such file or directory
cat: second: No such file or directory
cat: third: No such file or directory

If a variable is accessed, that is not set, nushell reports an error.

cat $env.UNSET_VARIABLE
output:

Error: nu::shell::column_not_found
  × Cannot find column 'UNSET_VARIABLE'
   ╭─[entry #11:1:5]
 1cat $env.UNSET_VARIABLE
   ·     ─────────┬─────────┬
   ·              │         ╰── value originates here
   ·              ╰── cannot find column 'UNSET_VARIABLE'
   ╰────

If we want to fallback to another variable, we can use the default command explicitly in nushell:

cat ($env.UNSET_VARIABLE? | default "some-default-value")
output:
cat: some-default-value: No such file or directory

Complimentary tooling and resources

Prompting

Nushell supports the prompting tool called Starship.
Starship provides a series of modules which can be used within your custom prompt.

This is my prompt, it is inspired by the powerlevel10k zsh theme: nushell prompt

Resources

There is a community curated github repository, nu_scripts, which contains a variety or scripts to extend nushell capabilities even further.

The Nushell project provides a great documentation for Nushell and the nu language.

Summary

In my opinion, the behaviour of nushell is more predictable and maintainable. If I make an error during the construction of a script I get direct feedback instead of a random crash because something else changed in a CICD pipeline or a deployment. Nushell comes with a huge amount of built-in tools and commands, e.g., a http client, a parser for all kinds of object notations and tools for system information queries. There is also a standalone musl build version of the nushell which is ideal for usage in devops environments, because it does not require any additional resources. It does not even need any additional dependencies apart from the linux kernel. Furthermore, nushell is cross platform, it also works with Windows and Mac Os X. Nushell is a rather new tool in my figurative toolbox; but one I cannot live without anymore.