You pipe JSON from an API into grep, then cut, then awk, and the script breaks the day a field moves. Nushell skips the text parsing. Every command hands the next one a table, a list, or a record, so you filter and sort by column name instead of by character position.
This guide covers the 20% of Nushell that does 80% of the work: tables and pipelines, opening data files, records and lists, finding help, variables, custom commands with def, and your configuration file. A searchable command reference and a curated set of videos, podcasts, books, and online resources close the article.
What You’ll Learn
- When Nushell is the right tool, and when bash or zsh still fits better.
- How tables flow through a pipeline, so
where,select, andsort-bymake sense. - How to open JSON, CSV, and TOML files and convert between formats.
- How records and lists work, and how to read a field.
- How to find help, store values in variables, and write your own commands.
- How to find and change your Nushell configuration.
When to Use Nushell
Nushell (often called nu) is a cross-platform shell that treats output as structured data. If you want the background on why it exists, read What Is Nushell? first. This guide sticks to the commands.
What Nushell Does Well
- Exploring JSON, CSV, TOML, and YAML files without a separate tool for each format.
- Filtering and sorting command output by column, such as files by size.
- Writing small scripts with typed parameters and clear error messages.
- Working the same way on Linux, macOS, and Windows.
Where Nushell Is Not the Right Tool
- Running existing bash scripts. Nushell is not POSIX compatible, so
.shfiles still need bash. - Pasting one-liners from bash tutorials. Syntax such as
$(...),&&chains, andexport VAR=valuediffers. - Shared team scripts, when the rest of the team doesn’t run Nushell.
Install Nushell with your package manager, then run nu to start it:
# macOS
brew install nushell
# Windows
winget install nushell
# Any platform with Rust installed
cargo install nu --lockedThe examples below use a small practice file, servers.json, holding five servers with a name, region, status, and CPU percentage. The Practice Nushell section shows how to create it in your own terminal.
Tables and Pipelines
Most Nushell commands return a table. Open the practice file and you see rows and named columns instead of raw text:
open servers.json╭───┬──────────┬────────────┬─────────┬─────╮
│ # │ name │ region │ status │ cpu │
├───┼──────────┼────────────┼─────────┼─────┤
│ 0 │ web-01 │ us-west │ running │ 42 │
│ 1 │ web-02 │ us-west │ stopped │ 0 │
│ 2 │ db-01 │ us-east │ running │ 71 │
│ 3 │ cache-01 │ eu-central │ running │ 18 │
│ 4 │ batch-01 │ us-east │ stopped │ 0 │
╰───┴──────────┴────────────┴─────────┴─────╯The pipe | passes that table to the next command. Use where to keep matching rows and select to keep only the columns you need:
open servers.json | where status == "running" | select name cpu╭───┬──────────┬─────╮
│ # │ name │ cpu │
├───┼──────────┼─────┤
│ 0 │ web-01 │ 42 │
│ 1 │ db-01 │ 71 │
│ 2 │ cache-01 │ 18 │
╰───┴──────────┴─────╯Sort by any column with sort-by, and take the top rows with first:
open servers.json | sort-by cpu --reverse | first 2╭───┬────────┬─────────┬─────────┬─────╮
│ # │ name │ region │ status │ cpu │
├───┼────────┼─────────┼─────────┼─────┤
│ 0 │ db-01 │ us-east │ running │ 71 │
│ 1 │ web-01 │ us-west │ running │ 42 │
╰───┴────────┴─────────┴─────────┴─────╯Built-in commands work the same way. ls returns a table of files, so you filter it by size with no awk in sight:
ls | where size > 4kb | get name╭───┬────────────╮
│ 0 │ access.log │
╰───┴────────────╯What you get: one habit, command | where ... | select ..., that works on files, processes, and data files alike.
Open Data Files
open reads a file and parses it by its extension. JSON, CSV, TOML, YAML, and more come back as tables and records:
open app.toml | get server.port8080open app.toml | get log.levelinfoCSV files open as tables too, so the same commands apply:
open sales.csv | first 3╭───┬────────────┬────────────┬─────────┬───────┬────────╮
│ # │ date │ region │ product │ units │ amount │
├───┼────────────┼────────────┼─────────┼───────┼────────┤
│ 0 │ 2026-03-01 │ us-west │ gadget │ 19 │ 228.00 │
│ 1 │ 2026-03-01 │ us-east │ widget │ 35 │ 665.00 │
│ 2 │ 2026-03-02 │ eu-central │ gizmo │ 36 │ 684.00 │
╰───┴────────────┴────────────┴─────────┴───────┴────────╯Count rows with length:
open sales.csv | where region == "eu-central" | length20Group rows by a column with group-by. The result is a record keyed by the column’s values, so columns lists the groups:
open servers.json | group-by region | columns╭───┬────────────╮
│ 0 │ us-west │
│ 1 │ us-east │
│ 2 │ eu-central │
╰───┴────────────╯Reduce a column to one number with the math commands:
open sales.csv | get units | math sum1317open servers.json | get cpu | math max71Files that Nushell doesn’t recognize, such as a log, open as text. Split text into a list of lines with lines:
open access.log | lines | first 2╭───┬──────────────────────────────────────────────────────────────────────────╮
│ 0 │ 10.0.0.222 - - [09/Jan/2026:08:00:00 +0000] "GET /api/servers HTTP/1.1" │
│ │ 500 5534 │
│ 1 │ 10.0.0.156 - - [09/Jan/2026:08:00:01 +0000] "GET /api/sales HTTP/1.1" │
│ │ 500 4982 │
╰───┴──────────────────────────────────────────────────────────────────────────╯A list of lines works with the same commands as any other list. Count the lines, then count the requests that got a 404:
open access.log | lines | length190open access.log | lines | where $it =~ " 404 " | length17Add --raw to skip parsing and get the file’s text, even for a format Nushell knows, such as Markdown:
open --raw notes.md | lines | first# Practice notesConvert between formats with the to commands, and write the result with save:
open servers.json | where status == "stopped" | to csvname,region,status,cpu
web-02,us-west,stopped,0
batch-01,us-east,stopped,0open servers.json | where status == "running" | save --force running.jsonsave picks the format from the file extension, so running.json gets JSON. --force replaces the file if it already exists. The from commands go the other way: they parse text you already have, such as the body of a web response, into a table or record.
If you know jq, where status == "running" does the job of jq’s select(.status == "running"). The Learn jq cookbook shows the jq side of the same structured-data ideas.
Records and Lists
A table is a list of records. A record is a set of named fields in braces, and a list is values in square brackets. Read a field with a dot:
let server = {name: "web-01", region: "us-west", cpu: 42}
$server.cpu42let regions = [us-west us-east eu-central]
$regions | length3get pulls one column out of a table as a list, or one row out as a record:
open servers.json | get name╭───┬──────────╮
│ 0 │ web-01 │
│ 1 │ web-02 │
│ 2 │ db-01 │
│ 3 │ cache-01 │
│ 4 │ batch-01 │
╰───┴──────────╯When a command does something unexpected, ask what kind of value you have with describe:
let server = {name: "web-01", region: "us-west", cpu: 42}
$server | describerecord<name: string, region: string, cpu: int>Get Help
Nushell documents itself. Run help with a command name to see its description, flags, and examples:
help wherehelp commands lists every command as a table, so you search it with the same where you use on data:
help commands | where name =~ "sort" | get name╭───┬─────────╮
│ 0 │ sort │
│ 1 │ sort-by │
╰───┴─────────╯help --find csv searches command descriptions as well as names, which helps when you know what you want to do but not the command’s name.
Variables
let creates a variable you can’t change. mut creates one you can. Prefix a variable with $ to read it:
mut total = 0
for s in (open servers.json) { $total += $s.cpu }
$total131Use a variable anywhere a value fits, such as a filter:
let threshold = 40
open servers.json | where cpu > $threshold | length2Reach for let first. Most of the time a pipeline gives you the value directly, and mut is only needed for loops like the one above.
Write Your Own Commands
def defines a command. Parameters can carry a type and a default value, and the command behaves like a built-in one, help text included:
def busy [threshold: int = 40] { open servers.json | where cpu > $threshold | get name }
busy
busy 60╭───┬───────╮
│ 0 │ db-01 │
╰───┴───────╯busy with no argument uses the default of 40 and lists web-01 and db-01. busy 60 lists only db-01, as shown. Commands you define at the prompt last until you close the shell. Put them in your configuration file to keep them.
Configure Nushell
Nushell keeps its configuration in config.nu. Print its location with $nu.config-path, and open it in your editor with config nu:
$nu.config-pathThe practice shell prints this path. On your machine, it starts with your own home directory:
/home/reader/.config/nushell/config.nuSettings live in the $env.config record. Change one for the current session by assigning to it:
$env.config.table.mode = "light"
open servers.json | first 2 # name region status cpu
──────────────────────────────────────
0 web-01 us-west running 42
1 web-02 us-west stopped 0To keep a setting, run config nu and add the same line, $env.config.table.mode = "light", to the end of config.nu.
Changes to config.nu apply to new sessions. Run nu to start a fresh session inside the current one, check the change, then run exit to return.
Practice Nushell
Nushell sticks once you type it. A practice shell that runs real Nushell in this page is on its way to this section. Until it arrives, run the drill below in your own terminal. It takes about ten minutes.
First, create the practice file in an empty directory. This one command builds a table from literal rows and saves it as JSON, unless the file is already there:
if not ("servers.json" | path exists) { [[name region status cpu]; [web-01 us-west running 42] [web-02 us-west stopped 0] [db-01 us-east running 71] [cache-01 eu-central running 18] [batch-01 us-east stopped 0]] | save servers.json }Then work through these steps:
- Run
open servers.jsonand read the table. - List the stopped servers with
where status == "stopped". - Show only the
nameandregioncolumns of the running servers. - Sort the servers by
cpu, highest first, and keep the top three. - Count the servers in
us-east. - Convert the running servers to CSV with
to csv. - Define a command named
in-regionthat takes a region and lists the server names in it, then runin-region eu-central.
Command Reference
Verified with Nushell 0.116.0. Search the reference to find a command fast. Each entry shows the command, then what it does.
Filter and Shape Tables
- where status == “running” - Keep the rows that match a condition
- where cpu > 40 - Keep the rows where a number column is above a value
- select name cpu - Keep only the named columns
- get name - Pull one column out of a table as a list
- sort-by cpu - Sort rows by a column, lowest first
- sort-by cpu –reverse - Sort rows by a column, highest first
- first 2 - Keep the first rows of a table or list
- length - Count the rows of a table or the items of a list
- group-by region - Group rows into a record keyed by a column’s values
- columns - List the column names of a table
Files and Formats
- open servers.json - Read a file and parse it by its extension
- open access.log | lines - Read a text file as a list of lines
- ls - List the files in the current directory as a table
- save running.json - Write the pipeline’s value to a file, in the format the extension names
- save –append notes.md - Add the pipeline’s value to the end of a file
- to csv - Convert a table to CSV text
- to json - Convert a value to JSON text
- to nuon - Convert a value to NUON, Nushell’s own data format
- from json - Parse JSON text into a table or record
- from csv - Parse CSV text into a table
Records, Lists, and Types
- {name: “web-01”, cpu: 42} - Create a record with named fields
- [3 1 2] - Create a list of values
- $server.cpu - Read a field from a record
- $regions | length - Count the items in a list
- describe - Show the type of a value
Help
- help where - Show a command’s description, flags, and examples
- help commands - List every command as a table you can filter
- help –find csv - Search command names and descriptions for a word
Variables and Commands
- let name = value - Create a variable you can’t change
- mut total = 0 - Create a variable you can change
- def busy [threshold: int = 40] { … } - Define a command with a typed parameter and a default
Configuration
- $nu.config-path - Print the path of your config.nu file
- config nu - Open config.nu in your editor
- $env.config - The record that holds every setting
- $env.config.table.mode = “light” - Change a setting for the current session
Examples
Each example runs on the practice file and shows what Nushell prints.
open servers.json | where region == "us-east" | get name╭───┬──────────╮
│ 0 │ db-01 │
│ 1 │ batch-01 │
╰───┴──────────╯open servers.json | first | to nuon{name: "web-01", region: us-west, status: running, cpu: 42}open servers.json | group-by status╭─────────┬────────────────╮
│ running │ [table 3 rows] │
│ stopped │ [table 2 rows] │
╰─────────┴────────────────╯Plugins and Networking
Nushell fetches web data with http get, and the response arrives already parsed. This example needs a network connection:
http get https://api.github.com/repos/nushell/nushell | get stargazers_count # runs on your machinePlugins add commands written in Rust or other languages. The Polars plugin adds fast dataframe commands for large CSV and Parquet files. Install it with Cargo, then register it with Nushell:
cargo install nu_plugin_polars --lockedplugin add ~/.cargo/bin/nu_plugin_polars # runs on your machine
plugin use polars # runs on your machineRun external programs as you would in any shell. When an external program shares a name with a Nushell command, prefix it with ^ to run the program instead:
^git status # runs on your machineLearn Nushell: Beyond the Basics
With pipelines, data files, and a custom command or two in hand, these resources take you further. Start with a video, then keep the Nushell Book open while you write your first scripts.
Video
- NuShell: A New Type of Shell by Better Stack, a short introduction to structured pipelines.
- Nushell in a Nutshell by DJ Ware, a beginner-oriented overview of the shell and its commands.
- Nushell for Beginners: A Better Linux Shell Than Bash? by ojamboshop, a first look aimed at bash users.
- How to Create Custom CLIs for Internal Developer Platforms with Nushell by DevOps & AI Toolkit, a longer walkthrough that builds real tools with
def.
Audio and Podcasts
- Nushell for the GitHub era on The Changelog, with creators Jonathan Turner, Andrés N. Robalino, and Yehuda Katz on why they built it.
- How to build a Nushell on Ship It!, with core maintainers Devyn Cairns and Jakub Žádník on building a cross-platform shell.
- Nushell with WindSoilder on Rustacean Station, a conversation with a Nushell contributor about the project’s Rust internals.
Books
- The Nushell Book, the free official guide from the Nushell team, from first commands to modules and scripts.
- Mastering Nushell: Modern Shell Scripting and Data Automation for Engineers and Analysts by Philip Oscar, a book-length treatment of scripting and data automation.
Online
- Coming from Bash, the official table that maps bash commands to their Nushell equivalents.
- Thinking in Nu, the official page on the habits that trip up newcomers.
- The Nushell command reference, every built-in command with its flags and examples.
- The Nushell Cookbook, task-focused recipes for files, tables, and HTTP.
- awesome-nu, a curated list of Nushell plugins, scripts, and tools.
Related Content
- What Is Nushell? explains why a shell built on structured data exists and when it fits.
- Learn jq covers the JSON filter many people use before they try Nushell.
- jq vs. yq compares two single-format tools that Nushell’s
opencommand replaces for many tasks. - Learn Tmux pairs with Nushell: run nu in one pane and your editor in another.
Related Articles by Category
🎓 Learn X
Begin learning new software frameworks, languages, tools, and techniques then leave with resources for further study.
💻 Development
Software development practices and techniques.

Comments #