Patterns#

A handful of patterns make Nu scripts safer and easier to live with. The big shifts from bash: errors are values you catch, variables are immutable by default, and the host-shell boundary (Nu ↔ external) is explicit, not silent.

Immutability First#

let is immutable. Reach for mut only when an accumulator genuinely needs to change in place. Pipelines should do almost all the work without state.

# idiomatic: transform without mutation
let total = (open users.json | get age | math sum)

# bash-style mutation, rare in idiomatic Nu
mut total = 0
for u in (open users.json) { $total = $total + $u.age }

The immutable form is also faster (no reallocation) and easier to refactor; mutable state shadows easily.

Error Handling#

Errors in Nu are structured values. try evaluates a body; catch receives the error record. Compose try / catch rather than reaching for set -e-style strict mode.

let result = try {
    open missing.json
} catch {
    {}                                          # default
}

try {
    http get https://target.example.com
} catch { |err|
    print -e $"failed: ($err.msg)"
    exit 1
}

Raise a structured error from your own code with error make.

def check [x: int] {
    if $x < 0 {
        error make {
            msg: "x must be non-negative"
            label: {text: "got negative", span: (metadata $x).span}
        }
    }
    $x
}

Cleanup#

Nu does not have trap in the bash sense. The conventional pattern is to wrap side-effecting work in a try and clean up in the catch / in a finally-style block after.

let tmp = (^mktemp | str trim)
try {
    # do work using $tmp
} catch { |err|
    print -e $"error: ($err.msg)"
}
^rm -f $tmp

For longer-lived sessions, a wrapper function that always cleans up.

def with-temp [closure: closure] {
    let tmp = (^mktemp | str trim)
    try {
        do $closure $tmp
    } catch { |err|
        ^rm -f $tmp
        error make {msg: $err.msg}
    }
    ^rm -f $tmp
}

Argument Parsing#

Native. Type the parameters on the def line; the parser handles the rest. Validation lives in attribute-free Nu code via the type system; --help works automatically.

def deploy [
    host: string                                # required positional
    --env: string = "dev"                       # flag with default
    --force(-f)                                 # boolean flag
    --port: int                                 # optional flag
] {
    if $env not-in [dev staging prod] {
        error make {msg: $"bad env: ($env)"}
    }
    print $"deploying ($host) to ($env)"
}

Host-Shell Boundary#

External programs (^cmd) exchange bytes with Nu. The operator usually wants to either keep work entirely in Nu (no externals) or convert at the boundary.

Bytes → structure.

^cat data.json | from json
^uname -a | str trim
^ls /tmp | lines

Structure → bytes.

$users | to json | ^curl -X POST -d @- https://api.example.com
$rows  | to csv  | ^psql -c "\copy table from stdin csv"

Forgetting to convert is the most common Nu beginner bug. When something silently produces [stream] or [binary 4096 bytes] instead of a real value, a missing from json / from csv / str trim is usually the cause.

Logging#

std/log ships with the standard library; use it instead of inventing a logging format.

use std

std log info "starting"
std log warning "retry"
std log error $"fatal: ($err.msg)"

The output goes to stderr in a structured form, with timestamps and level prefixes; the operator can redirect or filter.

Quoting#

Single quotes are literal. Double quotes do not interpolate unless prefixed with $. $"...($expr)..." is the interpolation form.

let name = "operator"
"hello $name"                                   # literal
$"hello ($name)"                                # hello operator

Pin Nu Version on Shared Scripts#

Nu is still pre-1.0 in spirit; breaking changes happen between minor versions. Scripts that need to run on someone else’s box should pin the version and verify on entry.

let need = "0.99.0"
if (version | get version) != $need {
    error make {msg: $"this script needs Nu ($need)"}
}

References#