A new type of shell
Go to file
Douglas 00709fc5bd
Improves startup time when using std-lib (#13842)
Updated summary for commit
[612e0e2](612e0e2160)
- While folks are welcome to read through the entire comments, the core
information is summarized here.

# Description

This PR drastically improves startup times of Nushell by only parsing a
single submodule of the Standard Library that provides the `banner` and
`pwd` commands. All other Standard Library commands and submodules are
parsed when imported by the user. This cuts startup times by more than
60%.

At the moment, we have stopped adding to `std-lib` because every
addition adds a small amount to the Nushell startup time.
With this change, we should once again be able to allow new
functionality to be added to the Standard Library without it impacting
`nu` startup times.

# User-Facing Changes

* Nushell now starts about 60% faster
* Breaking change: The `dirs` (Shells) aliases will return a warning
message that it will not be auto-loaded in the following release, along
with instructions on how to restore it (and disable the message)
* The `use std <submodule> *` syntax is available for convenience, but
should be avoided in scripts as it parses the entire `std` module and
all other submodules and places it in scope. The correct syntax to
*just* load a submodule is `use std/<submodule> *` (asterisk optional).
The slash is important. This will be documented.
* `use std *` can be used for convenience to load all of the library but
still incurs the full loading-time.
* `std/dirs`: Semi-breaking change. The `dirs` command replaces the
`show` command. This is more in line with the directory-stack
functionality found in other shells. Existing users will not be impacted
by this as the alias (`shells`) remains the same.

* Breaking-change: Technically a breaking change, but probably only
impacts maintainers of `std`. The virtual path for the standard library
has changed. It could previously be imported using its virtual path (and
technically, this would have been the correct way to do it):

  ```nu
  use NU_STDLIB_VIRTUAL_DIR/std
  ```

  The path is now simply `std/`:

  ```nu
  use std
  ```

  All submodules have moved accordingly.
  

# Timings

Comparisons below were made:

* In a temporary, clean config directory using `$env.XDG_CONFIG_HOME =
(mktemp -d)`.
* `nu` was run with a release build
* `nu` was run one time to generate the default `config.nu` (etc.) files
- Otherwise timings would include the user-prompt
* The shell was exited and then restarted several times to get timing
samples

(Note: Old timings based on 0.97 rather than 0.98, but in the range of
being accurate)

| Scenario | `$nu.startup-time` |
| --- | --- |
| 0.97.2
([aaaab8e](aaaab8e070))
Without this PR | 23ms - 24ms |
| This PR with deprecated commands | 9ms - <11ms |
| This PR after deprecated commands are removed in following release |
8ms - <10ms |
| Final PR (remove deprecated), using `--no-std-lib` | 6.1ms to 6.4ms |
| Final PR (remove deprecated), using `--no-config-file` | 3.1ms - 3.6ms
|
| Final PR (remove deprecated), using `--no-config-file --no-std-lib` |
1ms - 1.5ms |

*These last two timings point to the opportunity for further
optimization (see comment in thread below (will link once I write it).*

# Implementation details for future maintenance

* `use std banner` is a ridiculously deceptive call. That call parses
and imports *all* of `std` into scope. Simply replacing it with `use
std/core *` is essentially what saves ~14-15ms. This *only* imports the
submodule with the `banner` and `pwd` commands.

* From the code-comments, the reason that `NU_STDLIB_VIRTUAL_DIR` was
used as a prefix was so that there wouldn't be an issue if a user had a
`./std/mod.nu` in the current directory. This does **not** appear to be
an issue. After removing the prefix, I tested with both a relative
module as well as one in the `$env.NU_LIB_DIRS` path, and in all cases
the *internal* `std` still took precedence.

* By removing the prefix, users can now `use std` (and variants) without
requiring that it already be parsed and in scope.

* In the next release, we'll stop autoloading the `dirs` (shells)
functionality. While this only costs an additional 1-1.5ms, I think it's
better moved to the `config.nu` where the user can optionally remove it.
The main reason is its use of aliases (which have also caused issues) -
The `n`, `p`, and `g` short-commands are valuable real-estate, and users
may want to map these to something else.
  
For this release, there's an `deprecated_dirs` module that is still
autoloaded. As with the top-level commands, use of these will give a
deprecation warning with instructions on how to handle going forward.

To help with this, moved the aliases to their own submodule inside the
`dirs` module.

* Also sneaks in a small change where the top-level `dirs` command is
now the replacement for `dirs show`

* Fixed a double-import of `assert` in `dirs.nu`
* The `show_banner` step is replaced with simply `banner` rather than
re-importing it.

* A `virtual_path` may now be referenced with either a forward-slash or
a backward-slash on Windows. This allows `use std/<submodule>` to work
on all platforms.

# Performance side-notes:

* Future parsing and/or IR improvements should improve performance even
further.
* While the existing load time penalty of `std-lib` was not noticeable
on many systems, Nushell runs on a wide-variety of hardware and OS
platforms. Slower platforms will naturally see a bigger jump in
performance here. For users starting multiple Nushell sessions
frequently (e.g., `tmux`, Zellij, `screen`, et. al.) it is recommended
to keep total startup time (including user configuration) under ~250ms.

# Tests + Formatting

* All tests are green

* Updated tests:
- Removed the test that confirmed that `std` was loaded (since we
don't).
- Removed the `shells` test since it is not autoloaded. Main `dirs.nu`
functionality is tested through `stdlib-test`.
- Many tests assumed that the library was fully loaded, because it was
(even though we didn't intend for it to be). Fixed those tests.
- Tests now import only the necessary submodules (e.g., `use
std/assert`, rather than `use std assert`)
- Some tests *thought* they were loading `std/log`, but were doing so
improperly. This was masked by the now-fixed "load-everything-into-scope
bug". Local CI would pass due the `$env.NU_LOG_<...>` variables being
inherited from the calling process, but would fail in the "clean" GitHub
CI environment. These tests have also been fixed.

 * Added additional tests for the changes

# After Submitting

Will update the Standard Library doc page
2024-10-03 06:28:22 -05:00
.cargo optimize aarch64 when able (#10433) 2023-09-21 03:57:07 +12:00
.githooks Add git hooks for formatting and running clippy (#8820) 2023-04-13 07:34:23 -05:00
.github Bump crate-ci/typos from 1.24.6 to 1.25.0 (#13978) 2024-10-02 01:08:43 +00:00
assets REFACTOR: clean the root of the repo (#9231) 2023-05-20 07:57:51 -05:00
benches Make IR the default evaluator (#13718) 2024-09-15 14:54:38 -07:00
crates Improves startup time when using std-lib (#13842) 2024-10-03 06:28:22 -05:00
devdocs Remove dataframes crate and feature (#12889) 2024-05-20 17:22:08 +00:00
docker Simplify the Dockerfile (#13974) 2024-10-01 06:28:14 -05:00
scripts chore: Add nu_plugin_polars to build and install scripts (#13550) 2024-08-06 15:23:56 -05:00
src Make get_env_var return a reference to a Value (#13987) 2024-10-02 13:05:48 +02:00
tests Improves startup time when using std-lib (#13842) 2024-10-03 06:28:22 -05:00
wix change wix install method from perMachine to perUser (#12720) 2024-05-01 17:31:16 -05:00
.gitattributes Add Nushell Language detect for linguist (#9491) 2023-06-21 15:30:10 +08:00
.gitignore Add custom datetime format through strftime strings (#9500) 2023-06-23 15:05:04 -05:00
Cargo.lock Bump tempfile from 3.12.0 to 3.13.0 (#13984) 2024-10-02 06:17:03 +00:00
Cargo.toml Bump tempfile from 3.12.0 to 3.13.0 (#13984) 2024-10-02 06:17:03 +00:00
CITATION.cff Create CITATION.cff (#12983) 2024-06-06 08:53:34 +08:00
CODE_OF_CONDUCT.md First pass at updating all documentation formatting and cleaning up output of examples (#2031) 2020-06-24 06:21:47 +12:00
CONTRIBUTING.md Remove dataframes crate and feature (#12889) 2024-05-20 17:22:08 +00:00
Cross.toml Fix cross-compiling with cross-rs (#9972) 2023-08-09 22:08:35 -07:00
LICENSE Update LICENSE 2023-04-03 08:23:19 +12:00
README.md Update README.md (#13157) 2024-06-18 07:16:42 -05:00
rust-toolchain.toml bump rust version to 1.79.0 (#13809) 2024-09-08 16:15:54 -05:00
SECURITY.md Links to security contacts (#13488) 2024-07-30 17:05:56 +02:00
toolkit.nu ensure toolkit is using external cargo command (#13943) 2024-09-27 08:52:48 -05:00
typos.toml Bump typo with new ignore (#13563) 2024-08-08 06:50:28 +08:00

Nushell

Crates.io Build Status Nightly Build Discord The Changelog #363 @nu_shell GitHub commit activity GitHub contributors

A new type of shell.

Example of nushell

Table of Contents

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:

Packaging status

For details about which platforms the Nushell team actively supports, see our platform support policy.

Configuration

The default configurations can be found at sample_config which are the configuration files one gets when they startup Nushell for the first time.

It sets all of the default configuration to run Nushell. From here one can then customize this file for their specific needs.

To see where config.nu is located on your system simply type this command.

$nu.config-path

Please see our book for all of the Nushell documentation.

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  │
├───┼───────┼───────────┼───────┼───────────┼───────────┤
│ 02240 │ Slack.exe │ 16.40 │ 178.3 MiB │ 232.6 MiB │
│ 116948 │ Slack.exe │ 16.32 │ 205.0 MiB │ 197.9 MiB │
│ 217700 │ 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.

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.