Nushell

Updated: 08 October 2024

Nushell Docs

About

Nushell makes use of command outputs as data that can be transformed, it makes use of pipes that connnect commands together in a functional-programming usage style

Thinking in Nu

Nushell works with results using pipes, this is similar to > in bash but isn’t exactly the same

Immutability

Variables are immutable, however values can be shadowed, so I can create a shadowing x based on a previous x like so:

Terminal window
1
let x = $x + 1

Scoping

Nushell uses scoped environmments in blocks, so a command can use a value within its scope like so:

Terminal window
1
ls | each { |it|
2
cd $it.name
3
make
4
}

Fundamentals

Types of Data

The describe command returns the type of a variable

Terminal window
1
42 | describe

Conversions

The into command can convert variable types

Terminal window
1
"-5" | into int
Terminal window
1
"1.2" | into decimal

Strings

Strings can be created as:

  1. Double quotes: "hello world"
  2. Single quotes: 'hello: "world"'
  3. Interpolated: $"my number = (40 + 2)"
  4. Bare: hello

Bools

Booleans are simply true and false

Dates

Dates can be in the following formats:

  • 2022-02-02
  • 2022-02-02T14:30:00
  • 2022-02-02T14:30:00+05:00

Durations

Nushell has the following durations:

  • ns nanosecond
  • us microsecond
  • ms millisecond
  • sec second
  • min minute
  • hr hour
  • day day
  • wk week

And can be used like so:

Terminal window
1
3.14day

Or in calculations

Terminal window
1
30day / 1sec

Ranges

Ranges can be done as 1..3 for example, by default the end is inclusive, ranges can also be open ended ..2 or 2..

Records

Records hold key-value pairs, and may or may not have commas between entry names:

Terminal window
1
{name: john age: 5}

A record is the same as a single row of a table

Records can be iterated over by transposing them into a table:

Terminal window
1
{name: john age: 5} | transpose key value

And accessing properties can be done like:

Terminal window
1
{name: john age: 5}.age

Or

Terminal window
1
{name: john age: 5}."age"

Lists

Lists are ordered sequences of data and use [] with optional , separators. The below will create alist of strings

Terminal window
1
[sam fred george]

A list is the same as a single column table

Indexing lists can be done with a . as with records:

Terminal window
1
[sam fred george].1

Or using ranges:

Terminal window
1
[sam fred george] | range 0..1

Tables

Tables can be created with the following syntax:

Terminal window
1
[[column1, column2]; [Value1, Value2] [Value3, Value4]]

Tables can also be created from json

Terminal window
1
[{name: sam, rank: 10}, {name: bob, rank: 7}]

Internally tables are just a list of records

Blocks

Blocks of code are denoted using {}, for example:

Terminal window
1
each { |it| print $it }

Loading Data

Open Files

Files can be opened with the open command:

Terminal window
1
open package.json

Nu will parse the file if it can and will return data and not just a plain string

If a file extension isn’t what the type usually has, we can still parse the file, we just ned to tell nu that it’s a specific format, so we can do this like so:

Terminal window
1
open Cargo.lock | from toml

Manipulating Strings

String data can be manipulated using things like the lines command which will split each line into a row:

Terminal window
1
open people.txt | lines

And we can further apply the split command on the column to split it by some specific character:

Terminal window
1
open people.txt | lines | split column ";"

Additionally, we can use trim:

Terminal window
1
open people.txt | lines | split column ";" | str trim

And lastly, we can transform it into a table with formal column names with some additional properties on the split command:

Terminal window
1
open people.txt | lines | split column "|" first_name last_name job | str trim

Fetch Urls

We can also fetch remote files which will then also be converted into data like so:

Terminal window
1
fetch https://blog.rust-lang.org/feed.xml

Cheatsheet

Moving around the File System

Nushell provides commands for normal file-system related tasks which are similar to common commands such as:

Terminal window
1
./hello/world # will cd to the directory

Listing Files

Terminal window
1
ls

Or listing a specific file type

Terminal window
1
ls *.md

Or even globs

Terminal window
1
ls **/*.md

Globs

You can also use the glob method directly to find files recursively:

Terminal window
1
glob **/*.png

The glob method returns a list of strings versus the ls method which returns a list of file objects

Stopping All Docker Containers

The Docker CLI outputs data that’s nicely structured for working with the NuShell table structure.

We can kill all containers by parsing the data into a table and stopping them individually

Terminal window
1
docker container ls | from ssv | select "CONTAINER ID" | each { |i| docker container stop $i."CONTAINER ID" }

Config

Some utils from my current config.nu, primarily for working with Git

Terminal window
1
alias gch = git checkout
2
alias gcb = git checkout -b
3
alias glg = git log --graph
4
alias ga = git add
5
alias gp = git push
6
alias gf = git fetch
7
alias gl = git pull
8
alias gcm = git commit -m
9
alias gprune = git remote prune origin
10
11
alias conf = code $nu.config-path
12
alias env = code $nu.env-path
13
14
# Deletes all branches other than the current branch
15
def gclean [] {
16
git branch
17
| lines
18
| filter {|l| $l | str contains -n "*"}
19
| each {|b| $b | str trim}
20
| each {|b| git branch -d $b}
21
}
22
23
def 'gclean D' [] {
24
git branch
25
| lines
26
| filter {|l| $l | str contains -n "*"}
27
| each {|b| $b | str trim}
28
| each {|b| git branch -D $b}
29
}
30
31
def gmaster [] {
32
let branch = git rev-parse --abbrev-ref HEAD
33
git checkout master
34
git pull
35
git checkout $branch
36
git merge master
37
}
38
39
def dev [repo:string] {
40
code $"~/repos/$repo"
41
}
42
43
# Search for a string or regex using `rg -i`
44
def search [
45
regex:string, # regex or string to search on
46
-i # Run the search as case insensitive
47
] {
48
if $i {
49
rg -i $regex
50
} else {
51
rg $regex
52
}
53
}

Notifiy

A little script that’s also useful to have is this one that will notify you when a task completes. It’s a handy way to be notified when a long running task completes

This uses AppleScript as per the example on stackexchange so it will only work on MacOS. I’m sure there’s a similar way to accomplish this on other operating systems

Terminal window
1
def "notify" [title: string = "Task Complete"] {
2
print $in
3
4
let $command = 'display notification "' + $title + '" with title "Shell Process Complete" sound name "Frog"'
5
osascript -e $command
6
}

You can include the above in your nushell config and use it as follows:

Terminal window
1
my-command long-task | notify "My Long Task is Completed"

It will also handle printing the output from the task being run

The $in Param and Closures

Nushell functions can also use an implicit input parameter, this can be used when defining a function, for example:

Terminal window
1
def example[] {
2
echo $in
3
}

Which can then be used as

Terminal window
1
"Hello World!" | example

Additionally, note that example "Hello World!" will not work since $in params cannot be passed positionally and can only be used part of a pipeline

It’s also possible to use $in when we epect a closure which lets us leave out the parameter definition, for example, we can run ls in all subdirectories of an input like so:

Terminal window
1
# Using a normal closure
2
ls | each { |f| ls $f.name }
3
4
# Using `$in`
5
ls | each { ls $in.name }

The { ls $in.name } is the same as a closure like {|f| ls $f.name } so it’s a bit easier to type in this scenario as well.

Parsing

The parse function can be used to read some string into a usable data structure, take the following file for example:

1
john smith, age: 24
2
jack smith, age: 54

The parse command lets us structure that using:

1
open data.txt | lines | parse "{name} {surname}, age: {age}"
2
3
╭───┬──────┬─────────┬─────╮
4
│ # │ name │ surname │ age │
5
├───┼──────┼─────────┼─────┤
6
│ 0 │ john │ smith │ 24 │
7
│ 1 │ jack │ smith │ 54 │
8
╰───┴──────┴─────────┴─────╯

Input

You can take in user input using the input function, this allows for dynamic imput. This is handy for doing a search over some list, for example composing it with the above:

Terminal window
1
open data.txt | lines | parse "{name} {surname}, age: {age}" | input list 'Search for User' --fuzzy

Closures

Nushell does something quite interesting with closures. Since everything is immutable it’s possible to do environment-changing operations in a somewhat contained way.

For example, I can do some stuff like moving to a different folder, but I will not be affected outside of the closure

Terminal window
1
# in the `root` folder
2
do { cd ./my-child | ls } # within the closure i am inside of the `my-child` folder
3
# back to the `root` folder

Or I can cd into each folder and ls each of them, while remaining in my parent folder.

Terminal window
1
# in the `root` folder
2
ls | where type == dir | each { cd $in.name | ls }
3
# back to the `root` folder

Parallel

Due to the isolation that closures afford us, we can also run these in parallel, nushell has parallel methods of some commands, e.g. the each command, which can be used with par-each:

Terminal window
1
ls | where type == dir | par-each { cd $in.name | ls }

This works the same but is much faster for large/complex tasks

Timer

Nushell also has a timeit command that can be used to time the execution of any block, for example:

Terminal window
1
timeit { ls | each { print $in.name } }