8efbb48cb0
# Description Pick up [enhancement in `dtparse`](https://github.com/bspeice/dtparse/pull/41), to preserve nanoseconds in string-to-date conversions. # User-Facing Changes An especially eagle-eyed user might notice that prior versions of nushell were losing nanosecond precision when converting string format date/time to datetime. Before: ```nushell 〉'2023-03-02T01:02:03.987654321' | into datetime | date to-record ╭────────────┬───────────╮ │ year │ 2023 │ │ month │ 3 │ │ day │ 2 │ │ hour │ 1 │ │ minute │ 2 │ │ second │ 3 │ │ nanosecond │ 987654000 │ │ timezone │ -04:00 │ ╰────────────┴───────────╯ ``` Now, it just works(tm) ```nushell 〉'2023-03-02T01:02:03.987654321' | into datetime | date to-record ╭────────────┬───────────╮ │ year │ 2023 │ │ month │ 3 │ │ day │ 2 │ │ hour │ 1 │ │ minute │ 2 │ │ second │ 3 │ │ nanosecond │ 987654321 │ │ timezone │ -04:00 │ ╰────────────┴───────────╯ ``` # Tests + Formatting Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - [x] `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - [x] `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style - [x] `cargo test --workspace` to check that all tests pass Some (unrelated) tests did fail [^err] - [x] `cargo run -- crates/nu-std/tests.nu` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` # After Submitting If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. # (Unrelated) test failures noted [^err]: Several tests are failing, unrelated to this PR ``` failures: ---- modules::module_import_env_1 stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env main.nu; use main.nu foo; foo · ───┬─── · ╰── file not found ╰──── thread 'modules::module_import_env_1' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"foo"`', tests/modules/mod.rs:316:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ---- modules::module_import_env_2 stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env main.nu; $env.FOO · ───┬─── · ╰── file not found ╰──── thread 'modules::module_import_env_2' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"foo"`', tests/modules/mod.rs:341:9 ---- overlays::overlay_use_do_cd_file_relative stdout ---- === stderr thread 'overlays::overlay_use_do_cd_file_relative' panicked at 'assertion failed: `(left == right)` left: `"nushell"`, right: `"test1"`', tests/overlays/mod.rs:919:9 ---- overlays::overlay_use_dont_cd_overlay stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env test1/test2/spam.nu; $env.PWD | path basename · ─────────┬───────── · ╰── file not found ╰──── thread 'overlays::overlay_use_dont_cd_overlay' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"overlay_use_dont_cd_overlay"`', tests/overlays/mod.rs:946:9 ---- parsing::parse_file_relative_to_parsed_file_dont_use_cwd_1 stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env lol/lol.nu; $env.FOO · ─────┬──── · ╰── file not found ╰──── thread 'parsing::parse_file_relative_to_parsed_file_dont_use_cwd_1' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"good"`', tests/parsing/mod.rs:177:9 ---- parsing::parse_file_relative_to_parsed_file_simple stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env lol/lol/lol.nu; $env.LOL · ───────┬────── · ╰── file not found ╰──── thread 'parsing::parse_file_relative_to_parsed_file_simple' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"lol"`', tests/parsing/mod.rs:99:9 ---- shell::const_nu_lib_dirs_relative stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[main.nu:1:1] 1 │ const NU_LIB_DIRS = [ 'scripts' ] 2 │ source-env foo.nu · ───┬── thread 'shell::const_nu_lib_dirs_relative' panicked at 'assertion failed: outcome.err.is_empty()', tests/shell/mod.rs:166:9 ---- shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_lines stdout ---- === stderr thread 'shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_lines' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"AndrásWithKitKat"`', tests/shell/pipeline/commands/external.rs:198:13 ---- shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_strings stdout ---- === stderr thread 'shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_strings' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"jt_likes_cake.txt"`', tests/shell/pipeline/commands/external.rs:173:13 failures: modules::module_import_env_1 modules::module_import_env_2 overlays::overlay_use_do_cd_file_relative overlays::overlay_use_dont_cd_overlay parsing::parse_file_relative_to_parsed_file_dont_use_cwd_1 parsing::parse_file_relative_to_parsed_file_simple shell::const_nu_lib_dirs_relative shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_lines shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_strings test result: FAILED. 386 passed; 9 failed; 20 ignored; 0 measured; 0 filtered out; finished in 98.06s error: test failed, to rerun pass `-p nu --test main` failures: ---- modules::module_import_env_1 stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env main.nu; use main.nu foo; foo · ───┬─── · ╰── file not found ╰──── thread 'modules::module_import_env_1' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"foo"`', tests/modules/mod.rs:316:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ---- modules::module_import_env_2 stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env main.nu; $env.FOO · ───┬─── · ╰── file not found ╰──── thread 'modules::module_import_env_2' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"foo"`', tests/modules/mod.rs:341:9 ---- overlays::overlay_use_do_cd_file_relative stdout ---- === stderr thread 'overlays::overlay_use_do_cd_file_relative' panicked at 'assertion failed: `(left == right)` left: `"nushell"`, right: `"test1"`', tests/overlays/mod.rs:919:9 ---- overlays::overlay_use_dont_cd_overlay stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env test1/test2/spam.nu; $env.PWD | path basename · ─────────┬───────── · ╰── file not found ╰──── thread 'overlays::overlay_use_dont_cd_overlay' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"overlay_use_dont_cd_overlay"`', tests/overlays/mod.rs:946:9 ---- parsing::parse_file_relative_to_parsed_file_dont_use_cwd_1 stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env lol/lol.nu; $env.FOO · ─────┬──── · ╰── file not found ╰──── thread 'parsing::parse_file_relative_to_parsed_file_dont_use_cwd_1' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"good"`', tests/parsing/mod.rs:177:9 ---- parsing::parse_file_relative_to_parsed_file_simple stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env lol/lol/lol.nu; $env.LOL · ───────┬────── · ╰── file not found ╰──── thread 'parsing::parse_file_relative_to_parsed_file_simple' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"lol"`', tests/parsing/mod.rs:99:9 ---- shell::const_nu_lib_dirs_relative stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[main.nu:1:1] 1 │ const NU_LIB_DIRS = [ 'scripts' ] 2 │ source-env foo.nu · ───┬── thread 'shell::const_nu_lib_dirs_relative' panicked at 'assertion failed: outcome.err.is_empty()', tests/shell/mod.rs:166:9 ---- shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_lines stdout ---- === stderr thread 'shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_lines' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"AndrásWithKitKat"`', tests/shell/pipeline/commands/external.rs:198:13 ---- shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_strings stdout ---- === stderr thread 'shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_strings' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"jt_likes_cake.txt"`', tests/shell/pipeline/commands/external.rs:173:13 failures: modules::module_import_env_1 modules::module_import_env_2 overlays::overlay_use_do_cd_file_relative overlays::overlay_use_dont_cd_overlay parsing::parse_file_relative_to_parsed_file_dont_use_cwd_1 parsing::parse_file_relative_to_parsed_file_simple shell::const_nu_lib_dirs_relative shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_lines shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_strings test result: FAILED. 386 passed; 9 failed; 20 ignored; 0 measured; 0 filtered out; finished in 98.06s error: test failed, to rerun pass `-p nu --test main` failures: ---- modules::module_import_env_1 stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env main.nu; use main.nu foo; foo · ───┬─── · ╰── file not found ╰──── thread 'modules::module_import_env_1' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"foo"`', tests/modules/mod.rs:316:9 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ---- modules::module_import_env_2 stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env main.nu; $env.FOO · ───┬─── · ╰── file not found ╰──── thread 'modules::module_import_env_2' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"foo"`', tests/modules/mod.rs:341:9 ---- overlays::overlay_use_do_cd_file_relative stdout ---- === stderr thread 'overlays::overlay_use_do_cd_file_relative' panicked at 'assertion failed: `(left == right)` left: `"nushell"`, right: `"test1"`', tests/overlays/mod.rs:919:9 ---- overlays::overlay_use_dont_cd_overlay stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env test1/test2/spam.nu; $env.PWD | path basename · ─────────┬───────── · ╰── file not found ╰──── thread 'overlays::overlay_use_dont_cd_overlay' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"overlay_use_dont_cd_overlay"`', tests/overlays/mod.rs:946:9 ---- parsing::parse_file_relative_to_parsed_file_dont_use_cwd_1 stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env lol/lol.nu; $env.FOO · ─────┬──── · ╰── file not found ╰──── thread 'parsing::parse_file_relative_to_parsed_file_dont_use_cwd_1' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"good"`', tests/parsing/mod.rs:177:9 ---- parsing::parse_file_relative_to_parsed_file_simple stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[source:1:1] 1 │ source-env lol/lol/lol.nu; $env.LOL · ───────┬────── · ╰── file not found ╰──── thread 'parsing::parse_file_relative_to_parsed_file_simple' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"lol"`', tests/parsing/mod.rs:99:9 ---- shell::const_nu_lib_dirs_relative stdout ---- === stderr Error: nu:🐚:file_not_found × File not found ╭─[main.nu:1:1] 1 │ const NU_LIB_DIRS = [ 'scripts' ] 2 │ source-env foo.nu · ───┬── thread 'shell::const_nu_lib_dirs_relative' panicked at 'assertion failed: outcome.err.is_empty()', tests/shell/mod.rs:166:9 ---- shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_lines stdout ---- === stderr thread 'shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_lines' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"AndrásWithKitKat"`', tests/shell/pipeline/commands/external.rs:198:13 ---- shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_strings stdout ---- === stderr thread 'shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_strings' panicked at 'assertion failed: `(left == right)` left: `""`, right: `"jt_likes_cake.txt"`', tests/shell/pipeline/commands/external.rs:173:13 failures: modules::module_import_env_1 modules::module_import_env_2 overlays::overlay_use_do_cd_file_relative overlays::overlay_use_dont_cd_overlay parsing::parse_file_relative_to_parsed_file_dont_use_cwd_1 parsing::parse_file_relative_to_parsed_file_simple shell::const_nu_lib_dirs_relative shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_lines shell::pipeline::commands::external::it_evaluation::takes_rows_of_nu_value_strings test result: FAILED. 386 passed; 9 failed; 20 ignored; 0 measured; 0 filtered out; finished in 98.06s error: test failed, to rerun pass `-p nu --test main` ``` |
||
---|---|---|
.cargo | ||
.githooks | ||
.github | ||
assets | ||
benches | ||
crates | ||
docker | ||
docs | ||
images | ||
pkg_mgrs/winget | ||
src | ||
tests | ||
wix | ||
.gitignore | ||
.typos.toml | ||
build-all-maclin.sh | ||
build-all-windows.cmd | ||
build-all.nu | ||
build.rs | ||
Cargo.lock | ||
Cargo.toml | ||
CODE_OF_CONDUCT.md | ||
codecov.yml | ||
CONTRIBUTING.md | ||
coverage-local.nu | ||
coverage-local.sh | ||
Cross.toml | ||
install-all.ps1 | ||
install-all.sh | ||
LICENSE | ||
README.md | ||
README.release.txt | ||
register-plugins.nu | ||
rust-toolchain.toml | ||
toolkit.nu | ||
uninstall-all.sh |
Nushell
A new type of shell.
Table of Contents
- Status
- Learning About Nu
- Installation
- Philosophy
- Goals
- Progress
- Officially Supported By
- Contributing
- License
Status
This project has reached a minimum-viable-product level of quality. Many people use it as their daily driver, but it may be unstable for some commands. Nu's design is subject to change as it matures.
Learning About Nu
The Nushell book is the primary source of Nushell documentation. You can find a full list of Nu commands in the book, and we have many examples of using Nu in our cookbook.
We're also active on Discord and Twitter; come and chat with us!
Installation
To quickly install Nu:
# Linux and macOS
brew install nushell
# Windows
winget install nushell
To use Nu
in GitHub Action, check setup-nu for more detail.
Detailed installation instructions can be found in the installation chapter of the book. Nu is available via many package managers:
Philosophy
Nu draws inspiration from projects like PowerShell, functional programming languages, and modern CLI tools. Rather than thinking of files and data as raw streams of text, Nu looks at each input as something with structure. For example, when you list the contents of a directory what you get back is a table of rows, where each row represents an item in that directory. These values can be piped through a series of steps, in a series of commands called a 'pipeline'.
Pipelines
In Unix, it's common to pipe between commands to split up a sophisticated command over multiple steps. Nu takes this a step further and builds heavily on the idea of pipelines. As in the Unix philosophy, Nu allows commands to output to stdout and read from stdin. Additionally, commands can output structured data (you can think of this as a third kind of stream). Commands that work in the pipeline fit into one of three categories:
- Commands that produce a stream (e.g.,
ls
) - Commands that filter a stream (e.g.,
where type == "dir"
) - Commands that consume the output of the pipeline (e.g.,
table
)
Commands are separated by the pipe symbol (|
) to denote a pipeline flowing left to right.
> 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 │
╰────┴──────────┴──────┴─────────┴───────────────╯
Because most of the time you'll want to see the output of a pipeline, table
is assumed.
We could have also written the above:
> ls | where type == "dir"
Being able to use the same commands and compose them differently is an important philosophy in Nu.
For example, we could use the built-in ps
command to get a list of the running processes, using the same where
as above.
> ps | where cpu > 0
╭───┬───────┬───────────┬───────┬───────────┬───────────╮
│ # │ pid │ name │ cpu │ mem │ virtual │
├───┼───────┼───────────┼───────┼───────────┼───────────┤
│ 0 │ 2240 │ Slack.exe │ 16.40 │ 178.3 MiB │ 232.6 MiB │
│ 1 │ 16948 │ Slack.exe │ 16.32 │ 205.0 MiB │ 197.9 MiB │
│ 2 │ 17700 │ nu.exe │ 3.77 │ 26.1 MiB │ 8.8 MiB │
╰───┴───────┴───────────┴───────┴───────────┴───────────╯
Opening files
Nu can load file and URL contents as raw text or structured data (if it recognizes the format). For example, you can load a .toml file as structured data and explore it:
> open Cargo.toml
╭──────────────────┬────────────────────╮
│ bin │ [table 1 row] │
│ dependencies │ {record 25 fields} │
│ dev-dependencies │ {record 8 fields} │
│ features │ {record 10 fields} │
│ package │ {record 13 fields} │
│ patch │ {record 1 field} │
│ profile │ {record 3 fields} │
│ target │ {record 3 fields} │
│ workspace │ {record 1 field} │
╰──────────────────┴────────────────────╯
We can pipe this into a command that gets the contents of one of the columns:
> open Cargo.toml | get package
╭───────────────┬────────────────────────────────────╮
│ authors │ [list 1 item] │
│ default-run │ nu │
│ description │ A new type of shell │
│ documentation │ https://www.nushell.sh/book/ │
│ edition │ 2018 │
│ exclude │ [list 1 item] │
│ homepage │ https://www.nushell.sh │
│ license │ MIT │
│ metadata │ {record 1 field} │
│ name │ nu │
│ repository │ https://github.com/nushell/nushell │
│ rust-version │ 1.60 │
│ version │ 0.72.0 │
╰───────────────┴────────────────────────────────────╯
And if needed we can drill down further:
> open Cargo.toml | get package.version
0.72.0
Plugins
Nu supports plugins that offer additional functionality to the shell and follow the same structured data model that built-in commands use. There are a few examples in the crates/nu_plugins_*
directories.
Plugins are binaries that are available in your path and follow a nu_plugin_*
naming convention.
These binaries interact with nu via a simple JSON-RPC protocol where the command identifies itself and passes along its configuration, making it available for use.
If the plugin is a filter, data streams to it one element at a time, and it can stream data back in return via stdin/stdout.
If the plugin is a sink, it is given the full vector of final data and is given free reign over stdin/stdout to use as it pleases.
The awesome-nu repo lists a variety of nu-plugins while the showcase repo shows off informative blog posts that have been written about Nushell along with videos that highlight technical topics that have been presented.
Goals
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.
-
First and foremost, Nu is cross-platform. Commands and techniques should work across platforms and Nu has first-class support for Windows, macOS, and Linux.
-
Nu ensures compatibility with existing platform-specific executables.
-
Nu's workflow and tools should have the usability expected of modern software in 2022 (and beyond).
-
Nu views data as either structured or unstructured. It is a structured shell like PowerShell.
-
Finally, Nu views data functionally. Rather than using mutation, pipelines act as a means to load, change, and save data without mutable state.
Progress
Nu is under heavy development and will naturally change as it matures. The chart below isn't meant to be exhaustive, but it helps give an idea for some of the areas of development and their relative maturity:
Features | Not started | Prototype | MVP | Preview | Mature | Notes |
---|---|---|---|---|---|---|
Aliases | X | Aliases allow for shortening large commands, while passing flags | ||||
Notebook | X | Initial jupyter support, but it loses state and lacks features | ||||
File ops | X | cp, mv, rm, mkdir have some support, but lacking others | ||||
Environment | X | Temporary environment and scoped environment variables | ||||
Shells | X | Basic value and file shells, but no opt-in/opt-out for commands | ||||
Protocol | X | Streaming protocol is serviceable | ||||
Plugins | X | Plugins work on one row at a time, lack batching and expression eval | ||||
Errors | X | Error reporting works, but could use usability polish | ||||
Documentation | X | Book updated to latest release, including usage examples | ||||
Paging | X | Textview has paging, but we'd like paging for tables | ||||
Functions | X | Functions and aliases are supported | ||||
Variables | X | Nu supports variables and environment variables | ||||
Completions | X | Completions for filepaths | ||||
Type-checking | x | Commands check basic types, and input/output types |
Officially Supported By
Please submit an issue or PR to be added to this list.
Contributing
See Contributing for details. Thanks to all the people who already contributed!
License
The project is made available under the MIT license. See the LICENSE
file for more information.