Nushell 0.113.0
Today, we're releasing version 0.113.0 of Nu. This release adds fast in-memory indexing and search via idx, stream inspection without collection via peek, structured verbose output for file operations, and a more concise default output mode for from md.
Where to get it
Nu 0.113.0 is available as pre-built binaries or from crates.io. If you have Rust installed you can install it using cargo install nu.
As part of this release, we also publish a set of optional plugins you can install and use with Nushell.
Table of contents
Highlights and themes of this release [toc]
Fast file search with idx [toc]
@fdncred introduced a new idx command family for in-memory indexing and search. It gives you fast file and directory lookup (idx files, idx dirs, idx find) and even content search (idx search) without having to rescan every time.
You can also export and import snapshots, which is especially handy when you want to persist an index and restore it later.
idx init . --wait
idx find config --files
idx search --regex 'def\s+main'Take a look at all the commands here.
Stream detective mode: peek [toc]
Working with streams is one of Nushell's superpowers, and now you can inspect them without forcing a collect. The new peek command from @Bahex exposes metadata and sample values so you can make smarter pipeline decisions while keeping stream behavior intact.
.. | peek 1 | metadata access {|md|
if $md.peek.stream {
# keep streaming
} else {
$in
}
}See the detailed examples here.
Verbose file operations now return tables [toc]
Verbose filesystem commands are now much easier to script against, with improvements from @WindSoilder. Instead of only human-readable text, mkdir -v, mv -v, and rm -v now return structured table data you can filter, transform, and report on directly.
mkdir -v foo bar | where created == true
mv -v before.txt after.txt | get 0.message
rm -v missing.txt existing.txt | where deleted == falseRead more about mkdir/mv here and rm here.
Markdown, now with less AST noise [toc]
With work by @fdncred, from md now defaults to a more concise, human-friendly output mode, which makes it much nicer for day-to-day markdown processing. The previous full AST-style output is still available when you need deep detail via --verbose.
If you've been experimenting with markdown parsing since the last release, this should feel a lot more ergonomic out of the box.
Read more here.
Changes [toc]
Breaking changes [toc]
Updated kill signal shorthand handling [toc]
Note
This does not apply to Windows.
Allow kill -0 to show an error instead of killing nushell. Disallow commands like kill -9 pid because syntax should be kill -s 9 pid.
> kill -0
Error: nu::shell::incorrect_value
× Incorrect value.
╭─[repl_entry #2:1:1]
1 │ kill -0
· ──┬─ ─┬
· │ ╰── negative pid shorthand is not supported; use `kill -s 0 <pid>`
· ╰── encountered here
╰────Removed implicit line splitting of byte streams in parse [toc]
parse command, when receiving byte or string stream input (i.e. from an external process or file), used to implicitly split the input into lines. With this release it will collect such streams before processing them.
While the line-by-line behavior is closer to how tools like sed, grep and awk operate, it was confusing as parse worked differently between working with a string stream input and working with a string value input.
| Input |
| ||||
| Code | | ||||
| Output |
|
To process the input line-by-line all you need to do is run the stream through lines:
.. | lines | parse -r '...'mkdir -v now returns a table [toc] PR #18171 by @WindSoilder
Previously mkdir -v returned a string, now it returns a table.
> let x = mkdir -v foo bar
mkdir: created directory '/tmp/foo'
mkdir: created directory '/tmp/bar'
> $x
foo bar> let x = mkdir -v foo bar
> $x
╭───┬──────────┬─────────┬───────╮
│ # │ path │ created │ error │
├───┼──────────┼─────────┼───────┤
│ 0 │ /tmp/foo │ true │ │
│ 1 │ /tmp/bar │ true │ │
╰───┴──────────┴─────────┴───────╯> touch before.txt
> mv -v before.txt after.txt
╭───┬─────────────────┬────────────────┬────────────────────╮
│ # │ source │ destination │ message │
├───┼─────────────────┼────────────────┼────────────────────┤
│ 0 │ /tmp/before.txt │ /tmp/after.txt │ mv-verbose-renamed │
╰───┴─────────────────┴────────────────┴────────────────────╯Other breaking changes [toc]
from xlsxnow supports--header-rowfor specifying the 0-indexed index of the row from which to read column names. Default is the first non-empty row, which is a breaking change. Specify--header-row nullfor legacy behavior (i.e., no header). (#18189)
Additions [toc]
Preserve TOML comments when saving files [toc] PR #18008 by @galuszkak
TOML files now preserve comments, formatting, and inline tables when modified and saved.
open Cargo.toml
| update version "1.1.0"
| save Cargo.tomlThis should keep all comments intact.
Enhance ansi gradient command with named gradients and improved options [toc]
You can now use named gradients with ansi gradient.
> 'Nushell Gradient!' | ansi gradient --fgnamed rainbow
Nushell Gradient!> ansi gradient --list
╭────┬───────────┬──────────────────╮
│ # │ name │ text │
├────┼───────────┼──────────────────┤
│ 0 │ atlast │ Nushell Gradient │
│ 1 │ crystal │ Nushell Gradient │
│ 2 │ teen │ Nushell Gradient │
│ 3 │ mind │ Nushell Gradient │
│ 4 │ morning │ Nushell Gradient │
│ 5 │ vice │ Nushell Gradient │
│ 6 │ passion │ Nushell Gradient │
│ 7 │ fruit │ Nushell Gradient │
│ 8 │ retro │ Nushell Gradient │
│ 9 │ summer │ Nushell Gradient │
│ 10 │ rainbow │ Nushell Gradient │
│ 11 │ pastel │ Nushell Gradient │
│ 12 │ monsoon │ Nushell Gradient │
│ 13 │ forest │ Nushell Gradient │
│ 14 │ instagram │ Nushell Gradient │
╰────┴───────────┴──────────────────╯New commands for vi normal mode [toc]
Added two new commands to the vi normal modes:
o: inserts a newline below the cursor and enters insert modeO: inserts a newline above the cursor and enters insert mode
Their respective edit commands are InsertNewlineBelow and InsertNewlineAbove.
Terminal emulator progress bars via std-rfc/pb [toc]
Added a new module to std-rfc/pb, it wraps the OSC 9;4 escape sequences for setting progress bars in the terminal (some terminals don't support any/some of the commands).
Example:
use std-rfc/pb
try {
0..<10 | each {|x| sleep 200ms ; pb set-idx $x 10 }
} catch {
pb error
} finally {
pb clear
}New peek command for inspecting streams without collecting [toc]
Added peek command, which can be used to access properties of the pipeline input and get the first $n items of a list stream without collecting it.
It works by exposing this information as pipeline metadata so it can be retrieved with metadata access.
.. | peek | metadata access {|md|
if not $md.peek.stream {
$env.LAST_RESULT = $in
$in
} else {
# do not collect, pass the stream through as it is
}
}.. | peek 1 | metadata access {|md|
if (
$md.peek.value?.0? != null
and ($md.peek.value.0 | columns | $in has "type" and $in has "name")
) {
sort-by type name
} else {
# pass the stream through as it is
}
}Add additional polars selectors: ends-with, alpha, and alphanumeric [toc]
Introduces new polars selector sub commands
polars selector alphafor selecting columns alphabetic characterspolars selector alphanumericfor selecting columns with alphanumeric characterspolars selector ends-withfor selecting columns with a suffix
And even more polars selectors [toc]
Added 20 additional polars selectors:
polars selector array- Select all array columns. Optionally filter by fixed width.polars selector binary- Select all binary columns.polars selector boolean- Select all boolean columns.polars selector by-index- Select columns by their index position. Supports negative indices (e.g.,-1for the last column).polars selector categorical- Select all categorical columns.polars selector contains- Select columns whose names contain the given literal substring(s).polars selector date- Select all date columns.polars selector datetime- Select all datetime columns. Optionally filter by time unit (ns,us,ms) and/or timezone.polars selector decimal- Select all decimal columns.polars selector digit- Select columns whose names consist entirely of digit characters. By default uses Unicode decimal digits; use--ascii-onlyto restrict to ASCII0-9.polars selector duration- Select all duration columns. Optionally filter by time unit (ns,us,ms).polars selector empty- Create an empty selector that matches no columns. Useful as a base for selector composition.polars selector ends-with- Select columns that end with the given substring(s).polars selector enum- Select all enum columns.polars selector exclude- Select all columns except those with the given name(s). This is the inverse ofpolars selector by-name.polars selector list- Select all list columns.polars selector nested- Select all nested columns (list,array, orstruct).polars selector object- Select all object columns.polars selector starts-with- Select columns that start with the given substring(s).polars selector string- Select all string columns. Use--include-categoricalto also select categorical columns.polars selector struct- Select all struct columns.polars selector temporal- Select all temporal columns (date,datetime,duration, andtime).
Add idx family of commands for in-memory indexing [toc]
Add the idx command family for in-memory filesystem indexing and search.
Commands
idx init <path> [--wait]
Initializes the global idx runtime by scanning <path>. If the runtime is already initialized it is dropped and re-scanned. --wait blocks until the initial scan completes; omitting it returns immediately with a scanning = true status.
idx init .
idx init ~/projects --waitidx status
Returns a record describing the current runtime: initialized, base_path, watch, scanning, scan_duration_ms, files, dirs, and arena memory usage fields.
idx statusidx dirs
Returns all indexed directories as records with relative_path and full_path columns.
idx dirs
idx dirs | where full_path =~ srcidx files [<path>]
Returns all indexed files as records with relative_path, full_path, file_name, directory, size, and modified columns. If an optional <path> is provided it performs a direct lookup by relative or absolute path.
idx files
idx files src/main.rs
idx files /absolute/path/to/file.rsidx find <query> [--files] [--dirs] [--verbose] [--limit <n>]
Fuzzy-searches the index using fff-search scoring. By default searches both files and directories. --files or --dirs narrows the search. --verbose adds a score_details column. --limit caps the result count (default 100).
idx find main
idx find config --files --verbose
idx find src --dirs --limit 10idx search <pattern>... [--regex] [--fuzzy] [--limit <n>]
Searches the contents of indexed files. Plain text is the default. --regex enables regex matching. --fuzzy enables approximate line matching. Multiple patterns are searched with multi_grep. --limit caps matches (default 50).
idx search hello
idx search --regex 'fn \w+'
idx search TODO FIXME HACKidx export <filepath>
Persists the current runtime to a SQLite database at <filepath>. Returns a record with stored, path, file_count, dir_count, base_path, and format.
idx export ~/my-index.dbidx import <filepath>
Imports a SQLite snapshot created by idx export. Performs true offline restoration by hydrating the index from stored file and directory rows in the database with no filesystem rescanning. Returns a record with restored, source_path, base_path, watch, rehydration_mode, status, restored_files, restored_dirs, and format. The rehydration_mode is offline_from_snapshot_rows, and the status fields (scanning, scan_duration_ms).
idx import ~/my-index.dbidx drop
Drops the current runtime from memory. Returns {dropped: bool, status: record} where status is always the default (zeroed) status after the drop.
idx dropAllow filtering custom completions by description [toc]
Custom completers can now opt into matching the user's prefix against suggestion descriptions in addition to values, by setting match_description: true in the returned options record. The inserted completion is still the suggestion's value.
def "nu-complete users" [] {
{
options: { match_description: true, completion_algorithm: "substring" }
completions: [
{ value: "lk446763@example.com", description: "Lennart Kiil" }
{ value: "ab123456@example.com", description: "Alice Bob" }
]
}
}New std-rfc/iter prod command to produce cartesian products [toc]
A new command for producing cartesian products of an arbitrary number of lists, accessible as std-rfc/iter prod.
It takes a record with list values and returns a table where each column has items from the corresponding list:
> use std-rfc/iter prod
> prod {
size: [little, big]
color: [red, blue]
shape: [circle, box]
}
╭───┬────────┬───────┬────────╮
│ # │ size │ color │ shape │
├───┼────────┼───────┼────────┤
│ 0 │ little │ red │ circle │
│ 1 │ little │ red │ box │
│ 2 │ little │ blue │ circle │
│ 3 │ little │ blue │ box │
│ 4 │ big │ red │ circle │
│ 5 │ big │ red │ box │
│ 6 │ big │ blue │ circle │
│ 7 │ big │ blue │ box │
╰───┴────────┴───────┴────────╯It can also take an input stream, items of which will be in the "in" column:
> use std-rfc/iter prod
> [1, 2] | prod { rhs: [a, b] }
╭───┬────┬─────╮
│ # │ in │ rhs │
├───┼────┼─────┤
│ 0 │ 1 │ a │
│ 1 │ 1 │ b │
│ 2 │ 2 │ a │
│ 3 │ 2 │ b │
╰───┴────┴─────╯prod generates its output lazily, so it doesn't collect its input.
Added --no-commas flag to to nuon to exclude optional commas [toc]
When you use the new --no-commas flag on to nuon you won't have to see commas. Since nuon doesn't require commas, this will make the output more succinct. Some may argue that it's harder to read, which may be true, but nuon doesn't care about that.
> ls | to nuon --indent 2 --list-of-records | str length
1014
> ls | to nuon --indent 2 --list-of-records --no-commas | str length
983
> ls | to nuon | str length
802
> ls | to nuon --no-commas | str length
768Sometimes, every little bit helps.
The pre_prompt and env_change hooks may now modify the commandline [toc]
The pre_prompt and env_change hooks can now modify the commandline using the commandline edit command.
Example:
$env.config.hooks.pre_prompt ++= [{ commandline edit "test" }]rm -v returns structured data [toc] PR #18202 by @WindSoilder
> touch a
> rm -v z a c
╭───┬────────┬─────────┬───────────╮
│ # │ path │ deleted │ error │
├───┼────────┼─────────┼───────────┤
│ 0 │ /tmp/z │ false │ Not found │
│ 1 │ /tmp/c │ false │ Not found │
│ 2 │ /tmp/a │ true │ │
╰───┴────────┴─────────┴───────────╯rm removes other files even if there is an error in the middle
> touch a
> rm z a c
Error: nu::shell::io::not_found
× Not found
╭─[repl_entry #66:1:8]
1 │ rm z a c
· ┬
· ╰── Not found
╰────
help: '/tmp/c' does not exist
Error: nu::shell::io::not_found
× Not found
╭─[repl_entry #66:1:4]
1 │ rm z a c
· ┬
· ╰── Not found
╰────
help: '/tmp/z' does not exist
> 'a' | path exists
falseps -l has more columns [toc] PR #18255 by @WindSoilder
> ps -l | select pid name mem virtual working paged | first 5
╭───┬─────┬─────────────────┬──────────┬─────────┬──────────┬───────╮
│ # │ pid │ name │ mem │ virtual │ working │ paged │
├───┼─────┼─────────────────┼──────────┼─────────┼──────────┼───────┤
│ 0 │ 1 │ systemd │ 13.3 MB │ 22.2 MB │ 13.3 MB │ 0 B │
│ 1 │ 2 │ init-systemd(Ub │ 1.9 MB │ 2.8 MB │ 1.9 MB │ 0 B │
│ 2 │ 10 │ init │ 135.1 kB │ 2.8 MB │ 135.1 kB │ 0 B │
│ 3 │ 55 │ systemd-journal │ 17.8 MB │ 68.4 MB │ 17.8 MB │ 0 B │
│ 4 │ 100 │ systemd-udevd │ 6.2 MB │ 24.9 MB │ 6.2 MB │ 0 B │
╰───┴─────┴─────────────────┴──────────┴─────────┴──────────┴───────╯Added fish-style abbreviations [toc]
Added support for fish-style abbreviations that are expanded on enter or space. Abbreviations are syntax-aware in that expansion doesn't occur within strings or when appearing as arguments to an external program.
Abbreviations can be added to the config like so:
$env.config.abbreviations = {
ll: "ls -l"
gs: "git status"
}Locked history related config values during REPL [toc]
The following $env.config.history fields are now read-only after the REPL starts, since reedline only reads them once at startup and silently ignored later changes:
$env.config.history.path$env.config.history.max_size$env.config.history.file_format$env.config.history.isolation
sync_on_enter and ignore_space_prefixed remain mutable from the REPL since they are re-read on each prompt.
Attempting to change one of the locked fields from the REPL now produces a clear error instead of being silently ignored:
> $env.config.history.path = "/tmp/elsewhere.txt"
Error: nu::shell::invalid_config
× Encountered 1 error(s) when updating config
Error: nu::shell::config_option_locked_after_startup
× $env.config.history.path cannot be changed after Nushell has started
╭─[repl_entry #73:1:28]
1 │ $env.config.history.path = "/tmp/elsewhere.txt"
· ──────────┬─────────
· ╰── cannot be changed at runtime
╰────
help: set $env.config.history.path in your config.nu (or via --config / the relevant env var) and restart NushellOther additions [toc]
metadata access's closure can now modify the caller's environment, as if run withdo --env(#18085)- Introduced
std-rfc/str lcpcalculating longest common prefix for a list of strings (#17907) std-rfc/iter recursenow supports multiple cell-path arguments. (#18172)- Added the
std-rfc/urlmodule to modify urls more concisely. (#18239)
Deprecations [toc]
Deprecated watch command's optional closure argument [toc]
watch command's optional closure argument is deprecated and will be removed in a future release. Update your scripts to use a for-loop or each instead:
for event in (watch . --glob=**/*.rs) {
cargo test
}
# or
watch . --glob=**/*.rs | each { cargo test }As a reminder, using this streaming approach allows you to combine watch with other commands as well:
watch .
| where operation == Write and path like "*.md"
| each { md-lint $in.path }Deprecated implicit name column for grid command [toc]
The grid command now accepts an optional column name to display the contents of that column in a grid. One should no longer rely on the old behavior which automatically displays the name column as it is deprecated. Moreover, passing in a record as the input is also deprecated.
ls | grid
# or
{ name: test } | gridls | grid name
# or
[{ name: test }] | grid nameRemovals [toc]
Removed deprecated flag --datasource-ls of metadata set [toc]
The deprecated flag of metadata --datasource-ls has been removed. Use metadata set --path-columns [name] instead. The ls command will no longer set the source metadata to ls.
Other changes [toc]
to csv and to tsv stream now to save [toc] PR #18005 by @galuszkak
Saving streaming table data to .csv and .tsv now writes rows to disk as they arrive instead of buffering the full output first.
Streaming CSV/TSV export now fails with a clear schema-drift error if later rows introduce new columns after output has started. If your input has varying columns, use to csv --columns [...] / to tsv --columns [...] or collect first.
to yaml now correctly quotes string values [toc] PR #18010 by @galuszkak
> '{"value": "off", "listen": "0.0.0.0:8444"}' | from json | to yaml
value: off
listen: 0.0.0.0:8444
> '{"value": "off", "listen": "0.0.0.0:8444"}' | from json | to yaml
value: 'off'
listen: '0.0.0.0:8444'
Raise default promote-after to 2m and add timeout_secs param [toc] PR #18059 by @andrewgazelka
nu-mcp: default auto-promote-to-background threshold raised from 10s to 120s.nu-mcp: theevaluatetool accepts a new optionaltimeout_secsparameter to override the promotion threshold per call.
from md now produces a more concise output by default [toc]
The from md command now defaults to a more human friendly reduced mode. The original ast mode is now behind the --verbose switch.
Here are a couple screenshots of reduced mode.
> open AGENTS.md | first 10 | table --flatten
╭───┬─────────────┬─────────────────────────────────────────────────────┬───────────────────┬───────────────────╮
│ # │ element │ content │ content_span │ attributes │
├───┼─────────────┼─────────────────────────────────────────────────────┼───────────────────┼───────────────────┤
│ 0 │ text │ AGENTS.md │ {record 2 fields} │ {record 2 fields} │
│ 1 │ text │ Testing and code style rules │ {record 2 fields} │ {record 2 fields} │
│ 2 │ text │ Before every commit: │ {record 2 fields} │ {record 2 fields} │
│ 3 │ code_inline │ nu -c "use toolkit.nu; toolkit fmt" │ {record 2 fields} │ {record 2 fields} │
│ 4 │ text │ and │ {record 2 fields} │ {record 2 fields} │
│ 5 │ code_inline │ nu -c "use toolkit.nu; toolkit clippy" │ {record 2 fields} │ {record 2 fields} │
│ 6 │ text │ . This is pretty fast so you can run it frequently. │ {record 2 fields} │ {record 2 fields} │
│ 7 │ text │ Run tests: │ {record 2 fields} │ {record 2 fields} │
│ 8 │ code_inline │ nu -c "use toolkit.nu; toolkit test" │ {record 2 fields} │ {record 2 fields} │
│ 9 │ text │ or │ {record 2 fields} │ {record 2 fields} │
╰───┴─────────────┴─────────────────────────────────────────────────────┴───────────────────┴───────────────────╯> open README.md | get 89 | table -e
╭──────────────┬─────────────────────────────────────────────────────────╮
│ element │ code │
│ content │ ls | where type == "dir" | table │
│ │ # => ╭────┬──────────┬──────┬─────────┬───────────────╮ │
│ │ # => │ # │ name │ type │ size │ modified │ │
│ │ # => ├────┼──────────┼──────┼─────────┼───────────────┤ │
│ │ # => │ 0 │ .cargo │ dir │ 0 B │ 9 minutes ago │ │
│ │ # => │ 1 │ assets │ dir │ 0 B │ 2 weeks ago │ │
│ │ # => │ 2 │ crates │ dir │ 4.0 KiB │ 2 weeks ago │ │
│ │ # => │ 3 │ docker │ dir │ 0 B │ 2 weeks ago │ │
│ │ # => │ 4 │ docs │ dir │ 0 B │ 2 weeks ago │ │
│ │ # => │ 5 │ images │ dir │ 0 B │ 2 weeks ago │ │
│ │ # => │ 6 │ pkg_mgrs │ dir │ 0 B │ 2 weeks ago │ │
│ │ # => │ 7 │ samples │ dir │ 0 B │ 2 weeks ago │ │
│ │ # => │ 8 │ src │ dir │ 4.0 KiB │ 2 weeks ago │ │
│ │ # => │ 9 │ target │ dir │ 0 B │ a day ago │ │
│ │ # => │ 10 │ tests │ dir │ 4.0 KiB │ 2 weeks ago │ │
│ │ # => │ 11 │ wix │ dir │ 0 B │ 2 weeks ago │ │
│ │ # => ╰────┴──────────┴──────┴─────────┴───────────────╯ │
│ │ ╭───────┬──────────────────╮ │
│ content_span │ │ │ ╭────────┬────╮ │ │
│ │ │ start │ │ line │ 96 │ │ │
│ │ │ │ │ column │ 1 │ │ │
│ │ │ │ ╰────────┴────╯ │ │
│ │ │ │ ╭────────┬─────╮ │ │
│ │ │ end │ │ line │ 114 │ │ │
│ │ │ │ │ column │ 4 │ │ │
│ │ │ │ ╰────────┴─────╯ │ │
│ │ ╰───────┴──────────────────╯ │
│ │ ╭──────┬───────╮ │
│ attributes │ │ lang │ shell │ │
│ │ ╰──────┴───────╯ │
╰──────────────┴─────────────────────────────────────────────────────────╯Improved argument handling for untyped rest parameters [toc]
Fixed wrapped command argument handling in def --wrapped [...rest] for untyped rest params:
- Nushell now parses
--key="value"and--key='value'in wrapped rest args like external args, removing quote wrappers around the value. - Nushell now expands
~in untyped wrapped rest args even when args are read directly (for example via$args | to nuon), not only when forwarding to an external command. - Explicitly typed string rest params
[...rest: string]keep prior literal behavior.
Added dynamic dispatch for % sigil commands [toc]
Nushell now supports dynamic percent builtin dispatch with forms like %($cmd) and %$cmd.
This preserves percent safety semantics while allowing runtime command selection:
- only builtins are eligible targets
- non-builtin/custom/alias targets are rejected
- arguments are forwarded as parsed values (not string-evaluated like shell eval)
This should work very similarly to dynamic dispatch of externals. The same model was followed to implement it.
Here's a couple examples that are also tests.
Built-ins resolve
> let cmd = 'echo'; %$cmd 'hello'
hello
> %('echo') 'world'
world
> let cmd = 'echo'; %($cmd) 'hello'
helloMissing commands fail
> let cmd = 'my_nonexistent_cmd'; %($cmd)
Error: nu::shell::command_not_found
× Command not found
╭─[repl_entry #94:1:33]
1 │ let cmd = 'my_nonexistent_cmd'; %($cmd)
· ───┬───
· ╰── command not found
╰────
Custom commands aren't built-in commands so they fail too
> def custom_cmd [] { 'nope' }; let cmd = 'custom_cmd'; %($cmd)
Error: nu::shell::command_not_found
× Command not found
╭─[repl_entry #96:1:55]
1 │ def custom_cmd [] { 'nope' }; let cmd = 'custom_cmd'; %($cmd)
· ───┬───
· ╰── command not found
╰────
Improved escape handling [toc]
Nushell handles escaping better with the back slash \ when use with double quotes or string interpolation with double quotes where escapes are interpreted.
Nushell now supports and validates more backslash escape forms consistently in string parsing, and reports clearer errors for invalid unicode/hex escapes (including better wording around missing } and unicode code point limits).
Parse and process escape sequences in a byte string.
Now, the following escape sequences are handled:
- Simple:
\n,\r,\t,\\,\",\' - Control:
\0,\a,\b,\e,\f - Hex:
\xHH(exactly 2 hex digits) - Unicode:
\u{XXXXXX}(1-6 hex digits, max 0x10FFFF) - Special:
\/,\(,\),\{,\},\$,\^,\#,\|,\~
Make env_hook case insensitive [toc] PR #18234 by @WindSoilder
$env.config.hooks.env_change.pwd = [{|| print "dir changed" }]
cd ..
# => No result$env.config.hooks.env_change.pwd = [{|| print "dir changed" }]
cd ..
# => nushell prints out `dir changed`.Additional changes [toc]
- Added conditional compilation to fix never used warning when compiling without the mcp feature. (#18056)
- Fixes
input listbacktab functionality by undoing previous selection before navigation. (#18068) - The
evaluateMCP tool no longer accepts atimeout_secsparameter. To override the promote-after timeout, set$env.NU_MCP_PROMOTE_AFTER(e.g.$env.NU_MCP_PROMOTE_AFTER = 10min) — the setting is REPL-sticky and applies to subsequent calls until changed. (#18070) - Improved language for the "Cannot find column ..." shell error to be more precise about missing data and suggest user actions to fix. (#18108)
- Refactor
--on-colsinpolars pivotfrom required to optional parameter; supply default values based on unique values of--oncolumns (#18140) whichandhistorycommand now also havepath_columnsmetadata in their output. So the paths will render like it does forlscommand. (#18020)- Fixed an issue where
cpcould not copy symbolic links as links. Nushell now supportscp -Pandcp --no-dereference, including for symlinks that point to directories. (#18162) - Make auto-cd accept paths with leading/trailing whitespace. (#18123)
- The
eachcommand now preserves the metadata of the output of the closure if the input is a scalar value. (#18201)
Bug fixes [toc]
Fixed internal sigil recursion error [toc]
Fixed an issue where if you used the built-in sigil you might get a recursion error like this. This no longer happens.
Previous error output
> def ls [] { %ls | move name --last }
> ls
Error: nu::shell::recursion_limit_reached
× Recursion limit (50) reached
╭─[repl_entry #10:1:11]
1 │ def ls [] { %ls | move name --last }
· ─────────────┬────────────
· ╰── This called itself too many times
╰────
http commands now respect the $env.NO_PROXY variable [toc]
http command now reads the environment variable NO_PROXY and supports the following patterns.
| Pattern | Behavior |
|---|---|
example.com | Literally match example.com, but not sub.example.com |
.example.com | Match sub.example.com and foo.sub.example.com, but not example.com. |
*.example.com | Exactly like .example.com |
* | Match everything |
Info
Multiple patterns can be used by separating them via comma and expressions that are not on the above form are ignored silently.
finally no longer suppresses error messages [toc] PR #18075 by @WindSoilder
For example:
> try { 1 / 0 } finally { print 'this finally' }
this finally
Error: nu::shell::division_by_zero
× Division by zero.
╭─[repl_entry #4:1:9]
1 │ try { 1 / 0 } finally { print 'this finally' }
· ┬
· ╰── division by zero
╰────
The return value of finally is the return value of try or catch [toc] PR #18075 by @WindSoilder
> let x = try { 13 } finally { print 'this finally'; 1 }
this finally
> $x == 3
false
> let x = try { 1 / 0 } catch { "xx" } finally { print 'this finally' }
this finally
> $x == "xx"
trueFixed Rename events in watch [toc]
Order of the paths reported for Rename events are now correct, whereas previously they were reported as if $new_path was renamed to $path.
Additionally if one of these paths happened to be outside of the watched directory, previously the event was not reported at all, now they are reported with the path outside of the watched directory as null.
Fixed issue causing try to execute code twice [toc] PR #18173 by @WindSoilder
Now the following code won't run print statement twice.
> do -i {try {}; print "Look, I ran twice!"; not_a }
Look, I ran twice!
Look, I ran twice!> do -i {try {}; print "Look!"; not_a }
Look!try blocks may now be interrupted with ctrl-c [toc] PR #18155 by @WindSoilder
Finally block will run if try block is interrupted with ctrl-c, for example:
# cc.nu
#!/usr/bin/env nu
"test content" | save -f cleanup-test.txt
print $"before try: cleanup-test.txt exists = ('cleanup-test.txt' | path exists)"
try {
^ping -n 30 127.0.0.1 # or `^ping -c 30 127.0.0.1` on Linux/macOS
} finally {
print "finally ran"
rm -f cleanup-test.txt
}
print $"after try: cleanup-test.txt exists = ('cleanup-test.txt' | path exists)"And run it with:
> nu cc.nuPress ctrl-c during printing, it'll output finally ran
Annotated default values typed as glob are now properly handled [toc]
Default values in commands will now be correctly parsed as glob type instead of string when annotated.
> def foo [--x: glob = *.x] {
$x | describe
}
> foo
string> def foo [--x: glob = *.x] {
$x | describe
}
> foo
globmkdir will try to create all directories, even if errors happened in the middle [toc] PR #18195 by @WindSoilder
> cd /tmp
> mkdir a
> chmod 400 a
> mkdir b a/c
Error: nu::shell::error
× Permission denied
╭─[repl_entry #102:1:9]
1 │ mkdir b a/c
· ─┬─
· ╰── Permission denied
╰────
> "b" | path exists
trueAlso noted that the errors are pointed to correct span. Previously it points to crates/nu-command/src/filesystem/umkdir.rs:95:48, which is not user friendly.
Empty blocks' output types are the same as their input types [toc]
Previously empty blocks (a command's body, if-else branch, match arm etc.) were inferred to return nothing regardless of their input type. This caused code like the following to fail type checking despite being correct:
def pass-through []: int -> int { }The type checker now correctly recognizes that empty blocks pass their inputs as they are.
Fixed hash-quoting [toc]
Fix the raw string prefix and suffix in certain situations.
Assuming this TOML:
# example.toml
name = "my-app"
version = "1.0.0"> open -r example.toml | to nuon --raw-strings
r#'# example.toml
name = "my-app"
version = "1.0.0"
'#> open -r example.toml | to nuon --raw-strings
r##'# example.toml
name = "my-app"
version = "1.0.0"
'##Other fixes [toc]
- Shebangs are now ignored when extracting the
descriptionfor a module. This affects the output in commands likehelp <module name>andscope modules. (#18106) - Fixed a bug of command-wide completer where explicit
returnexpressions in the completer commands disrupt the functionality. (#18009) - Fixed a bug of lossy parsing of incomplete pipelines end with
|, which is noticeable via REPL highlighting. (#17977) random choicefromstd-rfcnow properly reportslist<any> -> list<any>andlist<any> -> anyfor its type signature. (#18093)- Fixes the issue where
$env.AAAA = "aaaa"; $env.AAAA = ""; hide-env AAAAfailed to hideAAAA. After the empty-string assignment. (#18170) - Improve repl error handling when redirected like
nu > /dev/fullornu 2>/dev/full. (#18210)
Hall of fame [toc]
Thanks to all the contributors below for helping us solve issues, improve documentation, refactor code, and more! 🙏
| author | change | link |
|---|---|---|
| @Juhan280 | Clean up grid command implementation | #18021 |
| @cptpiepmatz | Add --include-ignored flag to custom test harness | #18034 |
| @Juhan280 | Add tests for grid command | #18022 |
| @Juhan280 | Fix failing test for grid command | #18065 |
| @andrewgazelka | Overhaul nu-mcp instructions sent to MCP clients | #18057 |
| @OneProgGit | Fixed a typo in nu-std crate. | #18066 |
| @andrewgazelka | n/a (docs-only change to MCP server instructions) | #18071 |
| @orbisai0security | Fix medium severity security issue in .github/workflows/release-msi.yml. | #18074 |
| @cptpiepmatz | Add Command: Any and CustomValue: Any | #18076 |
| @cptpiepmatz | Implement Debug for EngineState and derive more Debug impls | #18091 |
| @Bahex | Replace FindMapResult with ControlFlow | #18094 |
| @Bahex | Prefer borrowed strings | #18097 |
| @Bahex | Refactor/update some old tests to the newer in process style | #18096 |
| @nome | Consolidate and fix closure parameter handling | #16844 |
| @Bahex | Add test_table! macro for writing tests and examples easier | #18098 |
| @Bahex | Parameter type and string interpolation | #18095 |
| @Bahex | Refactor tests to avoid format! | #18127 |
| @Bahex | Assortment of small refactors | #18154 |
| @cptpiepmatz | Allow configuring version field in version command | #18144 |
| @Bahex | Add docs for get_vendor_autoload_dirs | #18163 |
| @WindSoilder | Fix clippy | #18174 |
| @132ikl | Gate idx import and export commands behind sqlite feature | #18181 |
| @Metbcy | Add table rendering benchmarks for nu-table (#7727) | #18179 |
| @Bahex | Add nu-heavy-utils crate and consolidate endian flag handling | #18184 |
| @vip892766gma | Fix duplicated words in comments | #18196 |
| @Bahex | Some more cleaning | #18186 |
| @quyentonndbs | Fix duplicated "that" in nu-parser comment | #18200 |
| @colinmparker | - N/A | #18088 |
| @cptpiepmatz | Fix CI not executing all tests on Windows | #18211 |
| @cptpiepmatz | Fix running tests for only nu-cli | #18216 |
| @Bahex | Mark watch tests serial and increase timeout | #18233 |
| @cptpiepmatz | Move merge implementation to nu-heavy-utils | #18231 |
| @Juhan280 | Make commandline command family work again in ExecuteHostCommand | #18204 |
| @blindFS | PathLikeKind enum for parse_path_like | #18237 |
| @fdncred | Add benchmarks for parser performance and throughput analysis | #18245 |
| @Bahex | Small refactors utilizing pattern matching for conciseness | #18242 |
| @Bahex | Stderr/stdout full tests should not read user config | #18253 |
| @Matthieu-LAURENT39 | Fix query web --as-table example | #18262 |
| @Bahex | peek returns the actual type of values | #18268 |
| @cptpiepmatz | Fixed some build issues | #18270 |
Full changelog [toc]
| author | title | link |
|---|---|---|
| @132ikl | Gate idx import and export commands behind sqlite feature | #18181 |
| @Bahex | New peek command for inspecting streams without collecting | #17796 |
| @Bahex | metadata access can modify $env | #18085 |
| @Bahex | refactor: replace FindMapResult with ControlFlow | #18094 |
| @Bahex | refactor(parser): parameter type and string interpolation | #18095 |
| @Bahex | Refactor/update some old tests to the newer in process style | #18096 |
| @Bahex | refactor: Prefer borrowed strings | #18097 |
| @Bahex | Add test_table! macro for writing tests and examples easier | #18098 |
| @Bahex | Fixed Rename events in watch | #18115 |
| @Bahex | refactor tests to avoid format! | #18127 |
| @Bahex | Removed implicit line splitting of byte streams in parse | #18141 |
| @Bahex | Deprecated watch command's optional closure argument | #18143 |
| @Bahex | assortment of small refactors | #18154 |
| @Bahex | Add docs for get_vendor_autoload_dirs | #18163 |
| @Bahex | feat(std-rfc/iter recurse): support multiple cell-paths | #18172 |
| @Bahex | feat(std-rfc/iter): new prod command for cartesian products | #18177 |
| @Bahex | add nu-heavy-utils crate and consolidate endian flag handling | #18184 |
| @Bahex | Some more cleaning | #18186 |
| @Bahex | Empty blocks' output types are the same as their input types | #18213 |
| @Bahex | test(watch): mark watch tests serial and increase timeout | #18233 |
| @Bahex | Small refactors utilizing pattern matching for conciseness | #18242 |
| @Bahex | test: stderr/stdout full tests should not read user config | #18253 |
| @Bahex | peek returns the actual type of values | #18268 |
| @Juhan280 | refactor!: remove deprecated DataSource::Ls | #18019 |
| @Juhan280 | Add path_columns metadata to which and history command | #18020 |
| @Juhan280 | refactor: clean up grid command implementation | #18021 |
| @Juhan280 | add tests for grid command | #18022 |
| @Juhan280 | Fix failing test for grid command | #18065 |
| @Juhan280 | fix: allow pre_prompt and env_change hooks to modify the commandline | #18119 |
| @Juhan280 | feat: accept optional column argument for grid command | #18187 |
| @Juhan280 | fix: make each maintain the metadata of closure result if input is scalar | #18201 |
| @Juhan280 | fix: make commandline command family work again in ExecuteHostCommand | #18204 |
| @Matthieu-LAURENT39 | Fix query web --as-table example | #18262 |
| @Metbcy | Add table rendering benchmarks for nu-table (#7727) | #18179 |
| @Mrfiregem | feat(std-rfc): add std-rfc/url module | #18239 |
| @OneProgGit | Fix typo | #18066 |
| @SunayKulkarni | fix: remove executable bit from AGENTS.md | #18243 |
| @Tyarel8 | fix warning when not compiling with mcp | #18056 |
| @Tyarel8 | feat(std-rfc): add progressbar osc 9;4 wrapper | #18073 |
| @Tyarel8 | fix(parser): parse default values of type glob correctly | #18111 |
| @WindSoilder | finally should not affect return value | #18075 |
| @WindSoilder | make ctrl-c works with try..finally while running script | #18155 |
| @WindSoilder | make mv -v and mkdir -v returns structured data | #18171 |
| @WindSoilder | fix try may causes other code run twice | #18173 |
| @WindSoilder | fix clippy | #18174 |
| @WindSoilder | mkdir: don't interrupt when error happened | #18195 |
| @WindSoilder | rm: returns structure data when -v is used, and don't interrupt when error happened | #18202 |
| @WindSoilder | make env_hook case insensitive | #18234 |
| @WindSoilder | ps -l: introduce working_size, paged_size on windows and linux system | #18255 |
| @andrewgazelka | Overhaul nu-mcp instructions sent to MCP clients | #18057 |
| @andrewgazelka | nu-mcp: raise default promote-after to 2m and add timeout_secs param | #18059 |
| @andrewgazelka | nu-mcp: remove timeout_secs param, rely on NU_MCP_PROMOTE_AFTER | #18070 |
| @andrewgazelka | docs(nu-mcp): prefer | complete over o+e>| complete | #18071 |
| @app/dependabot | build(deps): bump rand from 0.10.0 to 0.10.1 | #18035 |
| @app/dependabot | build(deps): bump crate-ci/typos from 1.45.0 to 1.45.1 | #18041 |
| @app/dependabot | build(deps): bump lean_string from 0.5.1 to 0.6.0 | #18042 |
| @app/dependabot | build(deps): bump hashbrown from 0.16.1 to 0.17.0 | #18043 |
| @app/dependabot | build(deps): bump crate-ci/typos from 1.45.1 to 1.45.2 | #18133 |
| @app/dependabot | build(deps): bump shadow-rs from 1.7.1 to 2.0.0 | #18134 |
| @app/dependabot | build(deps): bump data-encoding from 2.10.0 to 2.11.0 | #18135 |
| @app/dependabot | build(deps): bump lru from 0.17.0 to 0.18.0 | #18136 |
| @app/dependabot | build(deps): bump fancy-regex from 0.17.0 to 0.18.0 | #18138 |
| @app/dependabot | build(deps): bump crate-ci/typos from 1.45.2 to 1.46.0 | #18164 |
| @app/dependabot | build(deps): bump rmcp from 1.5.0 to 1.6.0 | #18166 |
| @app/dependabot | build(deps): bump openssl from 0.10.78 to 0.10.79 | #18167 |
| @app/dependabot | build(deps): bump crate-ci/typos from 1.46.0 to 1.46.1 | #18191 |
| @app/dependabot | build(deps): bump calamine from 0.34.0 to 0.35.0 | #18193 |
| @app/dependabot | build(deps): bump scraper from 0.26.0 to 0.27.0 | #18194 |
| @app/dependabot | build(deps): bump crate-ci/typos from 1.46.1 to 1.46.2 | #18246 |
| @app/dependabot | build(deps): bump rmcp from 1.6.0 to 1.7.0 | #18249 |
| @app/dependabot | build(deps): bump quick-xml from 0.39.2 to 0.40.1 | #18250 |
| @ayax79 | Add additional polars selectors: ends-with, alpha, and alphanumeric | #18114 |
| @ayax79 | Even more polars selectors | #18120 |
| @ayax79 | Removed claude settings ang ignore them | #18145 |
| @blindFS | refactor(parser): move the pipe tokens in AST 1 element ahead | #17977 |
| @blindFS | fix(completion): allow explicit return in command-wide completer | #18009 |
| @blindFS | refactor(parser): PathLikeKind enum for parse_path_like | #18237 |
| @casedami | feat: fish-style abbreviations | #18197 |
| @colinmparker | Consolidate internal Value mutation logic | #18088 |
| @colinmparker | (#17448) Update label and help text on CantFindCol error | #18108 |
| @cptpiepmatz | Update PR template | #18026 |
| @cptpiepmatz | Add --include-ignored flag to custom test harness | #18034 |
| @cptpiepmatz | Bump to 0.112.3 | #18055 |
| @cptpiepmatz | Add Command: Any and CustomValue: Any | #18076 |
| @cptpiepmatz | Implement Debug for EngineState and derive more Debug impls | #18091 |
| @cptpiepmatz | Update random choice type signature | #18093 |
| @cptpiepmatz | Allow configuring version field in version command | #18144 |
| @cptpiepmatz | Fix CI not executing all tests on Windows | #18211 |
| @cptpiepmatz | Fix running tests for only nu-cli | #18216 |
| @cptpiepmatz | Move merge implementation to nu-heavy-utils | #18231 |
| @cptpiepmatz | Fixed some build issues | #18270 |
| @develop7 | Introduce str lcp for std-rfc | #17907 |
| @fdncred | Enhance ansi gradient command with named gradients and improved options | #18058 |
| @fdncred | update nushell to latest reedline commit 4ffb1d3 | #18067 |
| @fdncred | update Rust toolchain to 1.93.1 + some deps | #18077 |
| @fdncred | handle kill -0 as signal shorthand and update PID handling | #18081 |
| @fdncred | fix issue with sigil recursion | #18084 |
| @fdncred | bump reedline to latest commit | #18099 |
| @fdncred | refactor from md with --verbose for full ast and defaulting to a reduced mode | #18107 |
| @fdncred | Improved argument handling for untyped rest parameters | #18131 |
| @fdncred | Add idx family of commands for in-memory indexing | #18142 |
| @fdncred | Added dynamic dispatch for % sigil commands | #18158 |
| @fdncred | fix hide-env, refactor: rename remove_env_var to hide_env_var for clarity and update related logic | #18170 |
| @fdncred | add --no-commas flag to to nuon for compact output without comma separators | #18185 |
| @fdncred | fix(repl): improve error handling and cleanup on unexpected errors | #18210 |
| @fdncred | improve escape handling | #18217 |
| @fdncred | update deps for idx commands | #18224 |
| @fdncred | feat(parser): add benchmarks for parser performance and throughput analysis | #18245 |
| @fdncred | fix hash quoting and add tests | #18258 |
| @freepicheep | Fix module description parsing with leading shebang | #18106 |
| @galuszkak | feat(streaming): stream CSV/TSV output through 'save' and fail fast on schema drift implements #14338 | #18005 |
| @galuszkak | fix(toml): Preserve TOML comments when saving files #4296 | #18008 |
| @galuszkak | fix: losing string quoting for YAML values (Fixes #16072) | #18010 |
| @kaathewisegit | Fix backtabbing in --multi mode in input list | #18068 |
| @kiil | Allow filtering custom completions by description | #18156 |
| @kx0101 | feat: lock $env.config.history.path after REPL startup | #18190 |
| @m4siri | Respecting $env.NO_PROXY | #16730 |
| @nome | Consolidate and fix closure parameter handling | #16844 |
| @orbisai0security | fix: using variable interpolation ... | #18074 |
| @orbisai0security | fix: Limit explore search query length to keep TUI responsive | #18251 |
| @pyz4 | feat(polars): pivot: provide default --on-cols value if not specified | #18140 |
| @pyz4 | feat(from xlsx): add support for header row | #18189 |
| @quyentonndbs | chore(parser): fix duplicated "that" in nu-parser comment | #18200 |
| @vip892766gma | docs: fix duplicated words in comments | #18196 |
| @vvvu | Fix cp --no-dereference for symlinks | #18162 |
| @ymcx | Trim whitespace from REPL input, byproduct is auto-cd with leading/trailing spaces works better | #18123 |