Loading...
Loading...
Essential patterns, idioms, and gotchas for writing Nushell code. Use when writing Nushell scripts, functions, or working with Nushell's type system, pipelines, and data structures. Complements plugin development knowledge with practical usage patterns.
npx skill4agent add ypares/agent-skills nushell-usage$in# ❌ WRONG - treats $in as first parameter
def my-func [list: list, value: any] {
$list | append $value
}
# ✅ CORRECT - declares pipeline signature
def my-func [value: any]: list -> list {
$in | append $value
}
# Usage
[1 2 3] | my-func 4 # Works correctly
my-func [1 2 3] 4 # ERROR! my-func doesn't take positional params# No pipeline input
def func [x: int] { ... } # (x) -> output
# Pipeline input only
def func []: string -> int { ... } # string | func -> int
# Both pipeline and parameters
def func [x: int]: string -> int { ... } # string | func x -> int
# Generic pipeline
def func []: any -> any { ... } # works with any input type# Filter with index
$list | enumerate | where {|e| $e.index > 5 and $e.item.some-bool-field}
# Transform with previous state
$list | reduce --fold 0 {|item, acc| $acc + $item.value}# Create record
{name: "Alice", age: 30}
# Merge records (right-biased)
$rec1 | merge $rec2
# Merge many records (right-biased)
[$rec1 $rec2 $rec3 $rec4] | into record
# Update field
$rec | update name {|r| $"Dr. ($r.name)"}
# Insert field
$rec | insert active true
# Insert field based on existing fields
{x:1, y: 2} | insert z {|r| $r.x + $r.y}
# Upsert (update or insert)
$rec | upsert count {|r| ($r.count? | default 0) + 1}
# Reject fields
$rec | reject password secret_key
# Select fields
$rec | select name age email# Tables are lists of records
let table = [
{name: "Alice", age: 30}
{name: "Bob", age: 25}
]
# Filter rows
$table | where age > 25
# Add column
$table | insert retired {|row| $row.age > 65}
# Rename column
$table | rename -c {age: years}
# Group by
$table | group-by status --to-table
# Transpose (rows ↔ columns)
$table | transpose name data# If expressions return values
let result = if $condition {
"yes"
} else {
"no"
}
# Match expressions
let result = match $value {
0 => "zero"
1..10 => "small"
_ => "large"
}# Optional fields with ?
$record.field? # Returns null if missing
$record.field? | default "N/A" # Provide fallback
# Check existence
if ($record.field? != null) { ... }# Try-catch
try {
dangerous-operation
} catch {|err|
print $"Error: ($err.msg)"
}
# Returning errors
def my-func [] {
if $condition {
error make {msg: "Something went wrong"}
} else {
"success"
}
}
# Check command success
let result = try { fallible-command }
if ($result == null) {
# Handle error
}
# Use complete for detailed error info for EXTERNAL commands (bins)
let result = (fallible-external-command | complete)
if $result.exit_code != 0 {
print $"Error: ($result.stderr)"
}# Closures capture environment
let multiplier = 10
let double_and_add = {|x| ($x * 2) + $multiplier}
5 | do $double_and_add # Returns 20
# Outer mutable variables CANNOT be captured in closures
mut sum = 0
[1 2 3] | each {|x| $sum = $sum + $x} # ❌ WON'T COMPILE
# Use reduce instead
let sum = [1 2 3] | reduce {|x, acc| $acc + $x}# each: transform each element
$list | each {|item| $item * 2}
# each --flatten: stream outputs instead of collecting
# Turns list<list<T>> into list<T> by streaming items as they arrive
ls *.txt | each --flatten {|f| open $f.name | lines } | find "TODO"
# each --keep-empty: preserve null results
[1 2 3] | each --keep-empty {|e| if $e == 2 { "found" }}
# Result: ["" "found" ""] (vs. without flag: ["found"])
# filter/where: select elements
# Row condition (field access auto-uses $it)
$table | where size > 100 # Implicit: $it.size > 100
$table | where type == "file" # Implicit: $it.type == "file"
# Closure (must use $in or parameter)
$list | where {|x| $x > 10}
$list | where {$in > 10} # Same as above
# reduce/fold: accumulate
$list | reduce --fold 0 {|item, acc| $acc + $item}
# Reduce without fold (first element is initial accumulator)
[1 2 3 4] | reduce {|it, acc| $acc - $it} # ((1-2)-3)-4 = -8
# par-each: parallel processing
$large_list | par-each {|item| expensive-operation $item}
# for loop (imperative style)
for item in $list {
print $item
}# Interpolation
$"Hello ($name)!"
$"Sum: (1 + 2)" # "Sum: 3"
# Split/join
"a,b,c" | split row "," # ["a", "b", "c"]
["a", "b"] | str join ", " # "a, b"
# Regex
"hello123" | parse --regex '(?P<word>\w+)(?P<num>\d+)'
# Multi-line strings
$"
Line 1
Line 2
"# Basic patterns
glob *.rs # All .rs files in current dir
glob **/*.rs # Recursive .rs files
glob **/*.{rs,toml} # Multiple extensionsglobfindls# Define module
module my_module {
export def public-func [] { ... }
def private-func [] { ... }
export const MY_CONST = 42
}
# Use module
use my_module *
use my_module [public-func MY_CONST]
# Import from file
use lib/helpers.nu *# Automatic $it expansion on left side
$table | where size > 100 # Expands to: $it.size > 100
$table | where name =~ "test" # Expands to: $it.name =~ "test"
# Works with: where, filter (DEPRECATED, use where), find, skip while, take while, etc.
ls | where type == file # Simple and readable$itls | where ($it.name | str downcase) =~ readme # Need $it here# Use $in or parameter name
$table | where {|row| $row.size > 100}
$table | where {$in.size > 100}
# Can be stored and reused
let big_files = {|row| $row.size > 1mb}
ls | where $big_files
# Works anywhere
$list | each {|x| $x * 2}
$list | where {$in > 10}each# ❌ Don't pass single records to each
let record = {a: 1, b: 2}
$record | each {|field| print $field} # Only runs once!
# ✅ Use items, values, or transpose instead
$record | items {|key, val| print $"($key): ($val)"}
$record | transpose key val | each {|row| ...}# These are different!
$list | my-func arg1 arg2 # $list piped, arg1 & arg2 as params
my-func $list arg1 arg2 # All three as positional params (if signature allows)# ❌ Error if field doesn't exist
$record.missing # ERROR
# ✅ Use ?
$record.missing? # null
$record.missing? | default "N/A" # "N/A"# Empty list/table checks
if ($list | is-empty) { ... }
# Default value if empty
$list | default -e $val_if_empty##default^grepgrepfindfind^findgreprgjq# Print intermediate values
$data | each {|x| print $x; $x} # Prints and passes through
# Inspect type
$value | describe
# Debug point
debug # Drops into debugger (if available)
# Timing
timeit { expensive-command }