nushell-usage について
nushell-usageは、Nushellユーザー向けの用法をまとめたスキルです。パイプライン、クロージャ、型シグネチャ、データ操作を扱い、混同しやすい入力の意味やよくある落とし穴も説明します。
主な機能
- 01パイプライン入力と引数の区別
- 02リスト、レコード、テーブル操作
- 03条件式、クロージャ、反復処理
- 04null可能なフィールドとエラーの処理
- 05モジュール、型シグネチャ、デバッグの説明
活用シーン
- 01パイプラインシグネチャ付きNushell関数の作成
- 02テーブル処理で行条件とクロージャを選択
- 03フィールド欠落やクロージャの変数捕捉問題の調査
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ ypares/ agent-skills --skill nushell-usageインストール後、Claude Code で/nushell-usageを入力して読み込まれたか確認します。
同梱ファイルreferences/advanced-patterns.mdreferences/type-system.md
詳細
Nushell の使用パターン
重要な区別
パイプライン入力とパラメータ
重要: パイプライン入力($in)は関数パラメータと互換性があるわけではありません!
# ❌ WRONG - treats $in as first parameter
def my-func [list: list, value: any] {
$list | append $value
}
# ✅ CORRECT - declares pipeline signature
def my-func [value: any]: list -> list {
$in | append $value
}
# Usage
[1 2 3] | my-func 4 # Works correctly
my-func [1 2 3] 4 # ERROR! my-func doesn't take positional params
これはクロージャにも当てはまります。
これが重要な理由:
- パイプライン入力は遅延評価できます(ストリーミング)
- パラメータは先行評価されます(メモリに読み込まれる)
- 呼び出し規約がまったく異なります
型シグネチャ
# No pipeline input
def func [x: int] { ... } # (x) -> output
# Pipeline input only
def func []: string -> int { ... } # string | func -> int
# Both pipeline and parameters
def func [x: int]: string -> int { ... } # string | func x -> int
# Generic pipeline
def func []: any -> any { ... } # works with any input type
一般的なパターン
リストの操作
# Filter with index
$list | enumerate | where {|e| $e.index > 5 and $e.item.some-bool-field}
# Transform with previous state
$list | reduce --fold 0 {|item, acc| $acc + $item.value}
レコードの操作
# Create record
{name: "Alice", age: 30}
# Merge records (right-biased)
$rec1 | merge $rec2
# Merge many records (right-biased)
[$rec1 $rec2 $rec3 $rec4] | into record
# Update field
$rec | update name {|r| $"Dr. ($r.name)"}
# Insert field
$rec | insert active true
# Insert field based on existing fields
{x:1, y: 2} | insert z {|r| $r.x + $r.y}
# Upsert (update or insert)
$rec | upsert count {|r| ($r.count? | default 0) + 1}
# Reject fields
$rec | reject password secret_key
# Select fields
$rec | select name age email
テーブルの操作
# Tables are lists of records
let table = [
{name: "Alice", age: 30}
{name: "Bob", age: 25}
]
# Filter rows
$table | where age > 25
# Add column
$table | insert retired {|row| $row.age > 65}
# Rename column
$table | rename -c {age: years}
# Group by
$table | group-by status --to-table
# Transpose (rows ↔ columns)
$table | transpose name data
条件付き実行
# If expressions return values
let result = if $condition {
"yes"
} else {
"no"
}
# Match expressions
let result = match $value {
0 => "zero"
1..10 => "small"
_ => "large"
}
Null 安全性
# Optional fields with ?
$record.field? # Returns null if missing
$record.field? | default "N/A" # Provide fallback
# Check existence
if ($record.field? != null) { ... }
エラー処理
# Try-catch
try {
dangerous-operation
} catch {|err|
print $"Error: ($err.msg)"
}
# Returning errors
def my-func [] {
if $condition {
error make {msg: "Something went wrong"}
} else {
"success"
}
}
# Check command success
let result = try { fallible-command }
if ($result == null) {
# Handle error
}
# Use complete for detailed error info for EXTERNAL commands (bins)
let result = (fallible-external-command | complete)
if $result.exit_code != 0 {
print $"Error: ($result.stderr)"
}
クロージャとスコープ
# Closures capture environment
let multiplier = 10
let double_and_add = {|x| ($x * 2) + $multiplier}
5 | do $double_and_add # Returns 20
# Outer mutable variables CANNOT be captured in closures
mut sum = 0
[1 2 3] | each {|x| $sum = $sum + $x} # ❌ WON'T COMPILE
# Use reduce instead
let sum = [1 2 3] | reduce {|x, acc| $acc + $x}
反復パターン
# each: transform each element
$list | each {|item| $item * 2}
# each --flatten: stream outputs instead of collecting
# Turns list<list<T>> into list<T> by streaming items as they arrive
ls *.txt | each --flatten {|f| open $f.name | lines } | find "TODO"
# each --keep-empty: preserve null results
[1 2 3] | each --keep-empty {|e| if $e == 2 { "found" }}
# Result: ["" "found" ""] (vs. without flag: ["found"])
# filter/where: select elements
# Row condition (field access auto-uses $it)
$table | where size > 100 # Implicit: $it.size > 100
$table | where type == "file" # Implicit: $it.type == "file"
# Closure (must use $in or parameter)
$list | where {|x| $x > 10}
$list | where {$in > 10} # Same as above
# reduce/fold: accumulate
$list | reduce --fold 0 {|item, acc| $acc + $item}
# Reduce without fold (first element is initial accumulator)
[1 2 3 4] | reduce {|it, acc| $acc - $it} # ((1-2)-3)-4 = -8
# par-each: parallel processing
$large_list | par-each {|item| expensive-operation $item}
# for loop (imperative style)
for item in $list {
print $item
}
文字列操作
# Interpolation
$"Hello ($name)!"
$"Sum: (1 + 2)" # "Sum: 3"
# Split/join
"a,b,c" | split row "," # ["a", "b", "c"]
["a", "b"] | str join ", " # "a, b"
# Regex
"hello123" | parse --regex '(?P<word>\w+)(?P<num>\d+)'
# Multi-line strings
$"
Line 1
Line 2
"
Glob パターン(ファイルマッチング)
# Basic patterns
glob *.rs # All .rs files in current dir
glob **/*.rs # Recursive .rs files
glob **/*.{rs,toml} # Multiple extensions
注: ファイル検索には find や ls よりも glob を推奨します。より効率的で、パターンのサポートも優れています。
モジュールシステム
# Define module
module my_module {
export def public-func [] { ... }
def private-func [] { ... }
export const MY_CONST = 42
}
# Use module
use my_module *
use my_module [public-func MY_CONST]
# Import from file
use lib/helpers.nu *
行条件とクロージャ
多くのコマンドは行条件またはクロージャのどちらかを受け付けます:
行条件(省略構文)
# Automatic $it expansion on left side
$table | where size > 100 # Expands to: $it.size > 100
$table | where name =~ "test" # Expands to: $it.name =~ "test"
# Works with: where, filter (DEPRECATED, use where), find, skip while, take while, etc.
ls | where type == file # Simple and readable
制限事項:
- 変数に格納できません
- 左辺のフィールドアクセスのみ自動展開されます
- 部分式には明示的な
$itが必要です:ls | where ($it.name | str downcase) =~ readme # Need $it here
クロージャ(完全な柔軟性)
# Use $in or parameter name
$table | where {|row| $row.size > 100}
$table | where {$in.size > 100}
# Can be stored and reused
let big_files = {|row| $row.size > 1mb}
ls | where $big_files
# Works anywhere
$list | each {|x| $x * 2}
$list | where {$in > 10}
使い分け:
- 行条件: 単純なフィールド比較(構文がより簡潔)
- クロージャ: 複雑なロジック、再利用可能な条件、ネストした操作
よくある落とし穴
単一レコードに対する each
# ❌ Don't pass single records to each
let record = {a: 1, b: 2}
$record | each {|field| print $field} # Only runs once!
# ✅ Use items, values, or transpose instead
$record | items {|key, val| print $"($key): ($val)"}
$record | transpose key val | each {|row| ...}
パイプと呼び出しの曖昧さ
# These are different!
$list | my-func arg1 arg2 # $list piped, arg1 & arg2 as params
my-func $list arg1 arg2 # All three as positional params (if signature allows)
オプションフィールド
# ❌ Error if field doesn't exist
$record.missing # ERROR
# ✅ Use ?
$record.missing? # null
$record.missing? | default "N/A" # "N/A"
空のコレクション
# Empty list/table checks
if ($list | is-empty) { ... }
# Default value if empty
$list | default -e $val_if_empty
高度なトピック
高度なパターンやより深い解説については、以下を参照してください:
- references/advanced-patterns.md - パフォーマンス最適化、遅延評価、ストリーミング、クロージャ、メモリ効率の良いパターン
- references/type-system.md - 完全な型システムガイド、変換、ジェネリクス、型ガード
ベストプラクティス
- 型シグネチャを使用する - エラーを早期に発見できます
- パイプラインを優先する - より慣用的で合成可能です
- コメントで文書化する - インラインには
#、宣言の上にも#でドキュメントコメントを記述します - 選択的にエクスポートする - 名前空間を汚染しないようにします
defaultを使用する - null や欠落を適切に処理します- 入力を検証する - 関数の先頭で型や範囲をチェックします
- 一貫した型を返す - null と値を予期せず混在させないようにします
- モジュールを使用する - 関連する関数を整理します
- 段階的にテストする - 複雑なパイプラインをステップバイステップで構築します
- 外部コマンドにはキャレットを付ける -
grepではなく^grepを使います。nushell のコマンドではないことが明確になり、曖昧さを避けられます。Nushell のコマンドは常に優先されます。例えばfindは通常の Unix のfindツールではありません:^findを使用してください。 - 必要に応じて専用の外部コマンドを使用する - 大量のファイルを検索する場合は
grepやrgのほうが依然として高速で、大きくネストした JSON 構造はjqのほうがはるかに速く処理されます
デバッグ手法
# Print intermediate values
$data | each {|x| print $x; $x} # Prints and passes through
# Inspect type
$value | describe
# Debug point
debug # Drops into debugger (if available)
# Timing
timeit { expensive-command }
外部リソース
コメント
Markdown 対応