Modules

Similar to many other programming languages, Nushell also has modules that let you import custom commands into a current scope. However, since Nushell is also a shell, modules allow you to import environment variables which can be used to conveniently activate/deactivate various environments.

Basics

A simple module can be defined like this:

> module greetings {
     export def hello [name: string] {
         $"hello ($name)!"
     }

     export def hi [where: string] {
         $"hi ($where)!"
     }
}

or in a file named the same as the module you want to create:

# greetings.nu

export def hello [name: string] {
    $"hello ($name)!"
}

export def hi [where: string] {
    $"hi ($where)!"
}

We defined hello and hi custom commands inside a greetings module.

The export keyword makes it possible to later import the commands from the module.

Similar to def, it is also possible to mark def-env with the export keyword (you can learn more about def-env in the Environment chapter).

Using modules

By itself, the module does not do anything. To use what the module exports, we need to use it.

> use greetings

> greetings hello "world"
hello world!

> greetings hi "there"
hi there!

The hello and hi commands are now available with the greetings prefix.

Importing symbols

In general, anything after the use keyword forms an import pattern which controls how the symbols are imported. The import pattern can be one of the following:

use greetings

Imports all symbols with the module name as a prefix (we saw this in the previous example).

use greetings hello

The hello symbol will be imported directly without any prefix.

use greetings [ hello, hi ]

Imports multiple symbols directly without any prefix.

use greetings *

You can also use the module name and the * glob to import all names directly without any prefix.

Module Files

Nushell lets you implicitly treat a source file as a module. Let's start by saving the body of the module definition into a file:

# greetings.nu

export def hello [name: string] {
    $"hello ($name)!"
}

export def hi [where: string] {
    $"hi ($where)!"
}

Now, you can call use directly on the file:

> use greetings.nu

> greetings hello "world"
hello world!

> greetings hi "there"
hi there!

Nushell automatically infers the module's name from the stem of the file ("greetings" without the ".nu" extension). You can use any import patterns as described above with the file name instead of the module name.

Local Custom Commands

Any custom commands defined in a module without the export keyword will work only in the module's scope:

# greetings.nu

export def hello [name: string] {
    greetings-helper "hello" "world"
}

export def hi [where: string] {
    greetings-helper "hi" "there"
}

def greetings-helper [greeting: string, subject: string] {
    $"($greeting) ($subject)!"
}

Then, in Nushell we import all definitions from the "greetings.nu":

> use greetings.nu *

> hello "world"
hello world!

> hi "there"
hi there!

> greetings-helper "foo" "bar"  # fails because 'greetings-helper' is not exported

Environment Variables

So far we used modules just to import custom commands. However, modules can also define an environment using export-env:

# greetings.nu

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

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

use will run the code inside the export-env block and merge its environment into the current scope:

> use greetings.nu

> $env.MYNAME
Arthur, King of the Britons

> greetings hello
hello Arthur, King of the Britons!

TIP

You might wonder why we can't just define let-env at the top of the module. The reason is that the export-env {...} block keeps its scope separate from the rest of the module which makes it more organized. You can put a complex code defining your environment without polluting the namespace of the module, for example:

export-env {
    def tmp [] { "tmp" }
    def other [] { "other" }

    let len = (tmp | str length)

    load-env {
        OTHER_ENV: (other)
        TMP_LEN: $len
    }
}

Only $env.TMP_LEN and $env.OTHER_ENV are preserved after evaluating the export-env module.

If you also want to keep your variables in separate modules and export its environment, you could try to export use it:

# purpose.nu
export use greetings.nu
export-env {let-env MYPURPOSE = "to build an empire."}

export def greeting_purpose [] {
    $"Hello ($env.MYNAME). My purpose is ($env.MYPURPOSE)"
}

and then use it

> use purpose.nu
> purpose greeeting_purpose

However, this won't work, because the module would not export its environment unless defined manually, like so:

# purpose.nu

# preserves its environment
export-env {
    use greetings.nu
    let-env MYPURPOSE = "to build an empire."
}

export def greeting_purpose [] {
    $"Hello ($env.MYNAME). My purpose is ($env.MYPURPOSE)"
}

Now, everything is exported properly

> use purpose.nu
> purpose greeting_purpose
Hello Arthur, King of the Britons. My purpose is to build an empire.

Exporting symbols

Apart from def and def-env, you can also export aliases and externs, giving you a way to only use these features when you need. Exporting externs also gives you the ability to hide custom completion commands in a module, so they don't have to be part of the global namespace.

Here's the full list of ways you can export:

  • export def - export a custom command
  • export def-env - export a custom environment command
  • export alias - export an alias
  • export extern - export a known external definition
  • export use - use definitions from a module and export them from this module

Hiding

Any custom command or alias, imported from a module or not, can be "hidden", restoring the previous definition. We do this with the hide command:

> def foo [] { "foo" }

> foo
foo

> hide foo

> foo  # error! command not found!

The hide command also accepts import patterns, just like use. The import pattern is interpreted slightly differently, though. It can be one of the following:

hide foo or hide greetings

  • If the name is a custom command or an environment variable, hides it directly. Otherwise:
  • If the name is a module name, hides all of its exports prefixed with the module name

hide greetings hello

  • Hides only the prefixed command / environment variable

hide greetings [hello, hi]

  • Hides only the prefixed commands / environment variables

hide greetings *

  • Hides all of the module's exports, without the prefix

Hiding Environment Variables

Environment variables can be hidden with hide-env:

> let-env FOO = "FOO"

> $env.FOO
FOO

> hide-env FOO

> $env.FOO  # error! environment variable not found!