Files
Darren Schroeder b6671f5cdc fix(ps): list every process and report the same columns on every platform (#19145)
## Description

This PR refactors the `ps` command and consolidates some code with
`nu_system::ProcessInfo` while also making it more portable having the
same columns on all operating systems with `ps` and `ps -l`. It also
fixes some bugs where all the processes weren't being seen on MacOS.

Also there was some debate as to whether 0 was valid for cpu. `cpu` is
the percent of one core used during a ~100ms sample, like `top`. Most
processes are idle during any 100ms, and `/bin/ps` agrees. Per-process
values won't always match `/bin/ps`: macOS's `%CPU` is the kernel's
decaying recent average (a process stopped after 3 seconds at 100% shows
0.0 five seconds later), and Linux's `ps` shows lifetime average CPU.


## User-facing changes (Release notes)

- `ps` now lists every process on macOS (zombies included) and Windows
(all but the System Idle Process), including other users' and protected
processes. Values the operating system doesn't make available for a
process (on macOS, the CPU, memory and threads of other users' processes
unless you run as root) are null instead of the process being left out.
- `ps` and `ps -l` now return the same columns on every platform. A
column is null when the platform or the process doesn't provide that
value.
  - `ps`: `pid ppid name user status cpu cpu_time mem`
- `ps -l` adds: `virtual private command exe start_time user_id
process_group_id session_id priority nice threads read written cwd
environment`
- New columns:
  - `cpu_time`: total CPU time the process has used.
- `private`: the memory only the process uses, the number Activity
Monitor and Task Manager show as "Memory".
- `nice`: the nice value; on Windows, the priority class on the nice
scale.
- `read` and `written`: bytes of storage I/O since the process started
(all I/O on Windows).
  - `exe`: the path of the executable.
- Also new on the platforms that were missing them: `user`, `user_id`,
`process_group_id`, `session_id`, `threads`, `cwd` and `environment`,
and `start_time` on the BSDs.
- Changed columns:
  - `user` is now in `ps`, and `virtual` is now only in `ps -l`.
  - `process_threads` is renamed to `threads`.
- `working` (always the same as `mem`) and `paged` are removed. Use
`private` for the memory a process uses by itself.
- Linux:
  - `cpu` is now measured to the nanosecond instead of in 10% steps.
- `name` is no longer cut to 15 characters when argv[0] or the
executable shows the full name.
- `cwd` and `environment` are null for processes you can't read (other
users' processes unless you are root); `cwd` used to be an empty string.
- Windows:
- `ps` now has a `status` column (`Running`, `Sleeping` or `Suspended`).
- `user_sid` is replaced by `user_id`, which holds the SID string (for
example `S-1-5-18`).
- `virtual` is now the virtual address space size, as on other
platforms.
  - `priority` is the base priority (8 for normal priority).
- `nice` is the priority class on the nice scale, as libuv and Node.js
map it (normal 0, high -14, idle 19).
- `process_group_id` is the console process group, the one
`CREATE_NEW_PROCESS_GROUP` starts and `GenerateConsoleCtrlEvent` sends
Ctrl+Break to.
- macOS:
- `cwd` is now the current working directory rather than the directory
the process started in.
- `status` now uses `Sleeping` instead of `Sleep` and `Waiting`,
matching the other platforms, and zombies are listed as `Zombie`.
  - `kernel_task` (pid 0) is now listed, as Activity Monitor does.
- FreeBSD, NetBSD and OpenBSD:
- Zombies and other processes whose arguments can't be read are now
listed, under the kernel's process name.
- On FreeBSD, `cpu` now includes threads that start or exit during the
sample.
  - On OpenBSD, `virtual` is no longer always 0.
- `command` shows newlines and tabs in arguments as spaces on Windows
and the BSDs too, as it already did on Linux and macOS.
- Null sorts after every number, so the examples now drop nulls first:
`ps | compact cpu | sort-by cpu | last 3`. Matching a text column that
can be null with `=~` is an error, so also drop those rows first: `ps |
compact status | where status =~ Run`.


## Additional notes

closes #18358

Let this serve as documentation about what the columns mean per
platform.

The column set follows what top, htop, btop, btm, procs, Task Manager
and Activity Monitor show:

| column | Linux | macOS | Windows | FreeBSD | NetBSD / OpenBSD |
| --- | --- | --- | --- | --- | --- |
| `user` | name of the effective uid | same | account of the token's SID
| same as Linux | same as Linux |
| `status` | `stat` state | thread states; zombie/stopped for all |
thread states | `ki_stat` | `p_stat` |
| `cpu` | process CPU-time clock (ns), or `stat` ticks | task times |
process times | `ki_runtime` | `p_rtime` |
| `cpu_time` | `utime + stime` | task times | user + kernel time |
`ki_runtime` | `p_rtime` (null for zombies) |
| `mem` | RSS | resident size | working set | RSS | RSS |
| `virtual` | VSZ | virtual size | virtual size | VSZ | VSZ |
| `private` | `RssAnon` | physical footprint (own processes) | private
working set | resident pages of anonymous mappings from
`KERN_PROC_VMMAP` (own processes) | null: no private-memory figure |
| `exe` | `/proc/<pid>/exe` (own processes) | `proc_pidpath` |
`QueryFullProcessImageNameW` | `KERN_PROC_PATHNAME` |
`KERN_PROC_PATHNAME`; null on OpenBSD, which doesn't expose it |
| `start_time` | `stat` | `kinfo_proc` | creation time | `ki_start` |
`p_ustart` (null for zombies) |
| `user_id` | effective uid | effective uid | SID string | `ki_uid` |
`p_uid` |
| `process_group_id` | `stat` | short BSD info | console process group
from the process parameters | `ki_pgid` | `p__pgid` |
| `session_id` | `stat` | `getsid` (null for kernel_task) | Terminal
Services session | `ki_sid` | `p_sid` |
| `priority` | kernel priority | task priority (own processes) | base
priority | `ki_pri.pri_level` | `p_priority` |
| `nice` | `stat` | `kinfo_proc` | priority class on the nice scale |
`ki_nice` | `p_nice` |
| `threads` | `stat` | task info (own processes) | snapshot |
`ki_numthreads` | `p_nlwps`; null on OpenBSD, whose `kinfo_proc` has no
thread count |
| `read` / `written` | storage bytes from `/proc/<pid>/io` (own
processes) | storage bytes from `proc_pid_rusage` (own processes) | all
I/O bytes, including pipes and network | null: the kernel counts block
operations, not bytes | null: the kernel counts block operations, not
bytes |
| `cwd` | `/proc/<pid>/cwd` (own processes) | `PROC_PIDVNODEPATHINFO`
(own processes) | process parameters | `KERN_PROC_CWD` (own processes) |
`KERN_PROC_CWD` (own processes) |
| `environment` | `/proc/<pid>/environ` (own processes) |
`KERN_PROCARGS2` (own processes) | process parameters | `KERN_PROC_ENV`
(own processes) | `KERN_PROC_ENV` (own processes) |

"Own processes" means the current user's processes, or every process
when running as root.

Every backend reads each column from the kernel. These have no kernel
interface to read from and stay null, with the reason in a comment where
the backend sets them: `read`/`written` on the BSDs, `private` on NetBSD
and OpenBSD, and `exe` and `threads` on OpenBSD.
2026-10-05 08:08:09 -05:00

13 KiB

Nushell

Crates.io Build Status Nightly Build Discord The Changelog #363 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; 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 start up 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  │ ppid │   name    │ user │ status  │  cpu  │  cpu_time  │   mem    │
# => ├───┼───────┼──────┼───────────┼──────┼─────────┼───────┼────────────┼──────────┤
# => │ 0 │  2240 │ 9872 │ Slack.exe │ dev  │ Running │ 16.40 │ 1min 12sec │ 178.3 MB │
# => │ 1 │ 16948 │ 9872 │ Slack.exe │ dev  │ Running │ 16.32 │      48sec │ 205.0 MB │
# => │ 2 │ 17700 │ 4632 │ nu.exe    │ dev  │ Running │  3.77 │       2sec │  26.1 MB │
# => ╰───┴───────┴──────┴───────────┴──────┴─────────┴───────┴────────────┴──────────╯

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.