🐙

Nushell のカスタムコマンド・モジュール・オーバーレイ

2022/12/06に公開

カスタムコマンド

Nushellには多数のbuilt-inコマンドが用意されていますが、足りない場合ユーザは自らカスタムコマンドを作成できます。
カスタムコマンドの書式は以下の通りで、コマンドラインもしくは.nu拡張子でスクリプトファイルから実行可能です。

[export] def function_name [parameter_list] {
    # body
    "return date."
}

exportは外部モジュールなどのスコープ外からアクセスする場合につけます。exportがない場合は、スコープ内[1]のみ定義したカスタムコマンドを使用できます。予約語defに続いて、コマンド名を、次にに引数リストの後にコマンド本体のブロックを書きます。上記の例ではこのカスタムコマンドの戻り値はコマンドで最後に評価された値(return data)となります。

呼び出し側の書式としては、カスタムコマンド名の後に、引数を書いていきます。

fuction_name parameter_list...

実際のカスタムコマンドの定義は以下のとおりです。引数には、指定した型チェックが行われます。引数の省略形として、下記のようにオプショナル、デフォルトがあります。フラグ指定は呼び出し側がコマンド引数として指定したフラグが引数として与えられます。

# コマンドの説明文
export def get-name [
    arg1        : string        # 引数名:型
    arg2?       : string        # オプショナル(呼び出し側は指定しなくていい引数)
    arg3 = "default parameter"  # デフォルト(呼び出し側が指定しない場合にデフォルト値を設定)
    --flag(-f)  : int           # フラグ指定(呼び出し側はフラグとして渡す場合、ロング形式[--flag]の名前で参照可能)
    ...arg4     : string        # Restパラメータ(不定数の引数を使いたい場合)
] {
    [$arg1 $arg2 $arg3 $flag]
    for $n in $name { # Restパラメータ処理
        $n
    }
}
引数の型チェックエラー例

引数の型チェックでエラーとなった例:

❯ def p [n:int] {"ok"};
❯ p "123"
Error: nu::parser::parse_mismatch (link)

  × Parse mismatch during operation.
   ╭─[entry #60:1:1]
 1 │ p "123"
   ·   ──┬──
   ·     ╰── expected int
   ╰────
カスタムコマンドの引数に指定できる型
any
block
cell-path
duration
path
expr
filesize
glob
int
math
number
operator
range
cond
bool
signature
string
variable
record
list
table
error

モジュール

モジュール(いつくかのカスタムコマンドをまとまたもの)として、一連のカスタムコマンドを定義する場合の書式は、複数のカスタムコマンドをまとめて、moduleでまとめます。もしくはmoduleを使わなくても暗黙的にはスクリプトファイルがモジュールとなり、ファイル名がモジュール名となります。

module module_name {
    [export] def function_name [parameter_list] {
        # body
    }
}

モジュールを使う場合は、useコマンドを使用します。書式は以下のとおりです。基本はすべてのシンボルをインポートします。特定のシンボル(カスタムコマンド)のみインポートする場合、モジュール名に続いてリスト形式で記述します。

use モジュールファイル名 [*|シンボルリスト]
use モジュール名 [*|シンボルリスト]

モジュールファイル名を指定する場合、$env.NU_LIB_DIRSにあれば、ファイル名のみで指定できます。
これまでは、カスタム コマンドをインポートするためだけにモジュールを使用していました。ただし、モジュールは export-envを使用して環境を定義することもできます。

# greetings.nu

export-env {
    let-env MYNAME = "Arthur, King of the Britons"
}

export def hello [] {
    $"hello ($env.MYNAME)"
}

export-envで定義された環境変数は、useコマンドでexport-env ブロック内のコードを実行し、その環境変数を現在のスコープにマージします。

> use greetings.nu

> $env.MYNAME
Arthur, King of the Britons

> greetings hello
hello Arthur, King of the Britons!

別のモジュールをインポートし、さらにエクスポートしたい場合は、export useを使います。

export一覧

export def - カスタム コマンドをエクスポートする
export def-env - カスタム環境コマンドをエクスポートします
export alias - エイリアスをエクスポートします
export extern - 既知の外部定義をエクスポートします
export use - モジュールの定義を使用し、このモジュールからエクスポートします

モジュールからインポートされたかどうかに関係なく、カスタム コマンドまたはエイリアスを「非表示」にして、以前の定義を復元することができます。 hide コマンドでこれを行います。

❯ def foo [] { "foo" }
❯ foo
foo
❯ hide foo
❯ foo
Error: nu::shell::external_command (link)

  × External command failed
   ╭─[entry #101:1:1]
 1 │ foo
   · ─┬─
   ·  ╰── did you mean 'for'?
   ╰────
  help: No such file or directory (os error 2)

`hide`, `hiee-env`

使用方法はuseコマンドと同じです。

hide モジュール名 [*|シンボルリスト]

同様に環境変数はhide-envで無効化となります。

エイリアス

Nushell のエイリアスは、単純なテキスト置換を行う方法を提供します。これにより、デフォルトの引数を含む長いコマンドの短縮名を作成できます。
書式は以下のとおりです。

alias エイリアス名 = 置き換え元コマンド
# もしくはパイプを使用する場合は
alias エイリアス名 = (置き換え元コマンド)

たとえば、ls | grid -c に展開される ll というエイリアスは次のとおりです。

alias ll = (ls | grid -c)
alias一覧

aliasコマンドで定義一覧が表示されます。以下に例を示します。

❯ alias
╭───┬──────────────────┬───────────────────────────────────────────────────╮
│ # │      alias       │                     expansion                     │
├───┼──────────────────┼───────────────────────────────────────────────────┤
│ 0 │ crawler          │ wget --recursive --level inf --no-clobber         │
│   │                  │ --random-wait --restrict-file-names=windows       │
│   │                  │ --convert-links --no-parent --adjust-extension    │
│ 1 │ penguin          │ ssh -p 55555 -l kawa90 192.168.11.65              │
│ 2 │ ws               │ python3 -m http.server --bind 127.0.0.1 (port)    │
│   │                  │ --directory .                                     │
│ 3 │ zenn_new_article │ npx zenn new:article                              │
│ 4 │ zenn_new_book    │ npx zenn new:book                                 │
│ 5 │ zenn_preview     │ npx zenn preview                                  │
╰───┴──────────────────┴───────────────────────────────────────────────────╯

オーバーレイ

オーバーレイは、必要に応じてアクティブ化および非アクティブ化できる定義 (カスタム コマンド、エイリアス、環境変数) の「レイヤー」として機能します。これらは、Python などの一部の言語に見られる仮想環境に似ています。

注: オーバーレイを理解するには、オーバーレイがモジュールの上に構築されるため、最初にモジュールを確認してください。

まず、Nushell にはゼロと呼ばれるデフォルトのオーバーレイが 1 つ付属しています。 overlay list コマンドを使用して、アクティブなオーバーレイを調べることができます。そこにリストされているデフォルトのオーバーレイが表示されます。

新しいオーバーレイを作成するには、まずモジュールが必要です:

❯ module spam {
    export def foo [] {
        "foo"
    }

    export alias bar = "bar"

    export-env {
        load-env { BAZ: "baz" }
    }
}

この章全体でこのモジュールを使用するため、オーバーレイがスパムを使用している場合は、スパムがこのモジュールを参照していると想定してください。

オーバーレイを作成するには、overlay use を呼び出します。これによりアクティブなオーバーレイが指定されたオーバーレイに変更されます。

❯ overlay list
╭───┬──────╮
│ 0 │ zero │
╰───┴──────╯
❯ module spam {
∙     export def foo [] {
∙         "foo"
∙     }
∙ 
∙     export alias bar = "bar"
∙ 
∙     export-env {
∙         load-env { BAZ: "baz" }
∙     }
∙ }
❯ overlay use spam
❯ foo
foo
❯ bar
bar
❯ $env.BAZ
baz
❯ overlay list
╭───┬──────╮
│ 0 │ zero │
│ 1 │ spam │
╰───┴──────╯

モジュールの定義を現在のスコープに取り込み、 use コマンドと同じ方法で export-env ブロックを評価しました (モジュールの章を参照)。
オーバーレイ定義がもう必要ない場合は、overlay hide を呼び出します。

❯ overlay hide spam
❯ overlay list
╭───┬──────╮
│ 0 │ zero │
╰───┴──────╯

オーバーレイにもスコープが設定されています。追加されたオーバーレイは、スコープの最後で削除されます。

❯ do { overlay use spam; foo }
foo
❯ overlay list
╭───┬──────╮
│ 0 │ zero │
╰───┴──────╯

オーバーレイを削除する最後の方法は、引数を指定せずに overlay hide を呼び出して、最後のアクティブなオーバーレイを削除することです。
新しい定義 (コマンド、エイリアス、環境変数) は、最後のアクティブなオーバーレイに記録されます。

オーバーレイは追加した内容を記憶し、削除してもその情報を保存します。これにより、異なるコンテキスト間で繰り返しスワップできます。
[! TIP]オーバーレイを追加した後、カスタム定義をオーバーレイに追加したくない場合があります。解決策は、カスタム変更を記録するためだけに使用される新しい空のオーバーレイを作成することです。この場合は、overlay newコマンドを使用します。

❯ overlay new scratchpad   # scratchpadが新しいオーバーレイとなり、
❯ def eggs [] { "eggs" }   # 以降定義するコマンドはscratchpadに属する。
❯ overlay list
╭───┬────────────╮
│ 0 │ zero       │
│ 1 │ scratchpad │
╰───┴────────────╯
❯ eggs
eggs
❯ overlay hide             # activeなscratchpadが削除される
❯ eggs                     # eggsは無効となる
Error: nu::shell::external_command (link)
overlay hide補足

オーバーレイに属するコマンド、エイリアス、環境変数が削除されますが、次のオプションで除外することができます。
-k, --keep-custom : カスタムコマンドは除外します。
-e, --keep-env 環境変数リスト:オーバーレイ内で定義された指定環境変数は除外します

Extern

外部コマンドの呼び出しは、Nushell をシェルとして使用する (そして多くの場合、Nushell を言語として使用する) ための基本的な部分です。ただし、Nushell の外部にあるコマンドは、Nushell が呼び出しのエラー、補完、または構文の強調表示できないという問題があります。

externキーワードを使用すると、Nushell の外部にあるコマンドの完全な署名を記述できるため、上記のすべての利点が得られます。定義の例として、config.nuにてextern定義されているgit pushは以下のとおりです。

 export extern "git push" [
     remote?: string@"nu-complete git remotes", # the name of the remote
     refspec?: string@"nu-complete git branches"# the branch / refspec
     --verbose(-v)                              # be more verbose
     --quiet(-q)                                # be more quiet
     --repo: string                             # repository
     --all                                      # push all refs
     --mirror                                   # mirror all refs
     --delete(-d)                               # delete refs
     --tags                                     # push tags (can't be used with --all or --mirror)
     --dry-run(-n)                              # dry run
     --porcelain                                # machine-readable output
     --force(-f)                                # force updates
     --force-with-lease: string                 # require old value of ref to be at this value
     --recurse-submodules: string               # control recursive pushing of submodules
     --thin                                     # use thin pack
     --receive-pack: string                     # receive pack program
     --exec: string                             # receive pack program
     --set-upstream(-u)                         # set upstream for git pull/status
     --progress                                 # force progress reporting
     --prune                                    # prune locally removed refs
     --no-verify                                # bypass pre-push hook
     --follow-tags                              # push missing but relevant tags
     --signed: string                           # GPG sign the push
     --atomic                                   # request atomic transaction on remote side
     --push-option(-o): string                  # option to transmit
     --ipv4(-4)                                 # use IPv4 addresses only
     --ipv6(-6)                                 # use IPv6 addresses only
   ]

これにより、内部コマンドと同じ記述構文がすべて提供され、フラグ、短いフラグ、位置パラメーター、型などを記述できることがわかります。上記のstring@"nu-complete git remotes"はカスタム補完と呼ばれるものです。別途説明します。

脚注
  1. 大抵カスタムコマンドを記載したスクリプトファイル内 ↩︎

Discussion