Nushell: A Modern Shell for DevOps

devops dev

Bash is 35 years old. Nushell reimagines what a shell could be with structured data, type safety, and modern ergonomics. Is it ready for production use?

What Is Nushell

Nushell (Nu) treats shell output as structured data instead of text:

# Bash: Parse text
ls -la | awk '{print $9, $5}'

# Nu: Query structured data
ls | select name size

Everything is a table. Pipelines transform tables.

Installation

# macOS
brew install nushell

# Linux
cargo install nu

# Windows
winget install nushell
# Start Nu
nu

Core Concepts

Everything Is Data

# ls returns a table
> ls
╭───┬──────────────┬──────┬──────────┬────────────────╮
 # │    name      │ type │   size   │    modified    │
├───┼──────────────┼──────┼──────────┼────────────────┤
 0 Cargo.toml file    890 B 2 days ago
 1 src dir    160 B 2 days ago
 2 README.md file   1.2 KB 3 days ago
╰───┴──────────────┴──────┴──────────┴────────────────╯

Pipelines Transform Tables

# Filter files larger than 1KB
> ls | where size > 1kb

# Sort by modification time
> ls | sort-by modified

# Select specific columns
> ls | select name size

# Chain operations
> ls | where type == "file" | sort-by size | last 5

Type-Safe Operations

# This errors instead of silently failing
> "hello" + 5
# Error: cannot add string and int

Common Operations

File Operations

# List with filtering
> ls **/*.py | where size > 10kb

# Find large files
> ls **/* | where type == "file" | sort-by size | last 10

# Disk usage
> du | sort-by physical | last 5

Process Management

# Processes as table
> ps | where cpu > 5 | select name cpu mem

# Kill by name
> ps | where name =~ "chrome" | each { |p| kill $p.pid }

HTTP Requests

# HTTP is built-in, returns structured data
> http get https://api.github.com/users/octocat | select login id type

# Parse JSON automatically
> http get https://api.example.com/data | get items | where status == "active"

Working with JSON

# Parse JSON file
> open data.json | get users | where age > 25

# Convert formats
> open data.yaml | to json

# Query nested structures
> open config.json | get database.connection.host

DevOps Use Cases

Docker Management

# List containers as structured data
> docker ps --format=json | from json | where status =~ "Up"

# Custom docker command
def docker-running [] {
    docker ps --format=json | lines | each { |line| $line | from json }
}

> docker-running | select Names Status Ports

Kubernetes

# Parse kubectl output
> kubectl get pods -o json | from json | get items | select metadata.name status.phase

# Find failing pods
> kubectl get pods -o json | from json | get items | where status.phase != "Running"

Log Analysis

# Parse structured logs
> open access.log | lines | parse "{ip} - - [{date}] \"{method} {path}\"" | 
    group-by method | transpose method count

# Analyze JSON logs
> open app.log | lines | each { |l| $l | from json } | 
    where level == "error" | 
    group-by error_type

Git Operations

# Commits as table
> git log --oneline -20 | lines | parse "{hash} {message}"

# Files changed in recent commits
> git diff --stat HEAD~5 | lines | parse "{file} | {changes}"

Custom Commands

# Define in config.nu or scripts
def greet [name: string] {
    $"Hello, ($name)!"
}

> greet "World"
Hello, World!

# With flags
def deploy [
    env: string,      # Environment to deploy
    --dry-run(-d)     # Don't actually deploy
] {
    if $dry_run {
        print $"Would deploy to ($env)"
    } else {
        # actual deployment
    }
}

Config and Environment

# Config file location
> $nu.config-path
/Users/username/.config/nushell/config.nu

# Environment variables
> $env.HOME
/Users/username

> $env.PATH | split row ":"

Config Example

# config.nu
$env.config = {
    show_banner: false
    table: {
        mode: rounded
    }
    completions: {
        quick: true
        partial: true
    }
}

# Aliases
alias ll = ls -l
alias k = kubectl
alias g = git

Integration with Traditional Tools

# Run external commands
> ^grep "error" app.log

# Capture as text
> ^cat file.txt | str trim

# Parse external output
> ^aws s3 ls | lines | parse "{date} {time} {size} {name}"

Scripting

#!/usr/bin/env nu

# deploy.nu
def main [env: string] {
    print $"Deploying to ($env)..."
    
    let pods = (kubectl get pods -o json | from json | get items)
    
    for pod in $pods {
        print $"  Processing ($pod.metadata.name)"
    }
    
    print "Done!"
}

Comparison

FeatureBashZshFishNushell
Structured dataNoNoNoYes
Type safetyNoNoNoYes
Built-in HTTPNoNoNoYes
Auto-completeManualManualYesYes
PipelinesTextTextTextTables
Learning curveMediumMediumLowMedium

Limitations

Not a POSIX Shell

# This Bash script won't work in Nu
for f in *.txt; do
    cat "$f"
done

You need to translate or call Bash explicitly.

Ecosystem

Performance

For simple commands, overhead is negligible. For massive data processing, specialized tools (awk, jq) may be faster.

Should You Switch?

Yes, If

Keep Bash/Zsh, If

Final Thoughts

Nushell represents what shells could be if designed today. Structured data changes everything about how you compose commands.

It’s not a Bash replacement—it’s a different tool for modern workflows.


The shell, reimagined for structured data.

All posts