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
| Feature | Bash | Zsh | Fish | Nushell |
|---|---|---|---|---|
| Structured data | No | No | No | Yes |
| Type safety | No | No | No | Yes |
| Built-in HTTP | No | No | No | Yes |
| Auto-complete | Manual | Manual | Yes | Yes |
| Pipelines | Text | Text | Text | Tables |
| Learning curve | Medium | Medium | Low | Medium |
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
- Fewer tutorials
- Less Stack Overflow coverage
- Some tools assume Bash
Performance
For simple commands, overhead is negligible. For massive data processing, specialized tools (awk, jq) may be faster.
Should You Switch?
Yes, If
- You work with structured data (JSON, YAML)
- DevOps/SRE work with APIs and config
- You want better error messages
- You enjoy modern CLI ergonomics
Keep Bash/Zsh, If
- Heavy legacy script usage
- Team needs POSIX compatibility
- Minimal CLI usage
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.