Home Explore Blog CI



nushell

1st chunk of `cookbook/foreign_shell_scripts.md`
4dfff7bc265f6583d5d06a97ef4c79366495b775dd1124ed0000000100000fa1
---
title: Foreign Shell Scripts
---

# Working With Foreign Shell Scripts

A common issue with nu is, that other applications export environment variables
or functionality as shell scripts, that are expected to then be evaluated by
your shell.

But many applications only consider the most commonly used shells like `bash` or
`zsh`. Unfortunately, nu has entirely incompatible syntax with these shells, so
it cannot run or `source` these scripts directly.

Generally nothing stops you from running a `zsh` script by invoking `zsh` itself
(given it is installed). But unfortunately this will not allow nu to access
exported environment variables:

```nu
# This works, using zsh to print "Hello"
'echo Hello' | zsh -c $in

# This exits with an error because $env.VAR is not defined
'export VAR="Hello"' | zsh -c $in
print $env.VAR
```

This chapter presents two workarounds for getting around this issue, and the
involved drawbacks.

---

## Parsing a Script as a String

A naive workaround to extract environment variable declarations is to read the
foreign script as a string and parse anything that looks like a variable
declaration, so it can be loaded into nushell's environment.

```nu
let bash_greeting = '
export GREETING="Hello";
export FROM="from bash";
'

load-env (
  $bash_greeting
  | str trim
  | lines
  | parse 'export {name}="{value}";'
  | transpose --header-row --as-record
)

print $"($env.GREETING) ($env.FROM)" # "Hello from bash"
```

This is perfectly fine for situations where you are sure of the exact format of
the script and can predict parsing edge cases.

This quickly gets tricky though, for example when the script is declaring a
`PATH` variable that references its previous value
(`export PATH="$PATH:/extra/path";`).

There are ways to implement some form of expansion too, but at some point it
might make more sense to leave the parsing to the shell it was meant for.

## Bash Env Plugin

There is a third-party Nu plugin [bash-env](https://github.com/tesujimath/nu_plugin_bash_env)
for importing environment variables from Bash format files and pipes.
This plugin uses Bash itself to parse the environment definitions,
and can therefore cope with arbitrarily complex Bash sources.

::: warning
Please note that the `bash-env` plugin is not supported by the core Nushell team.
All issues and requests for support should be directed to its own
[issue tracker](https://github.com/tesujimath/nu_plugin_bash_env/issues).
:::

## Capturing the environment from a foreign shell script

A more complex approach is to run the script in the shell it is written for and
then do some hackery to capture the script's variables afterwards.

Note: The shown command assumes a Unix-like operating system, it may also be
possible to implement one for Windows that could capture variables from a
PowerShell script.

```nu
# Returns a record of changed env variables after running a non-nushell script's contents (passed via stdin), e.g. a bash script you want to "source"
def capture-foreign-env [
    --shell (-s): string = /bin/sh
    # The shell to run the script in
    # (has to support '-c' argument and POSIX 'env', 'echo', 'eval' commands)
    --arguments (-a): list<string> = []
    # Additional command line arguments to pass to the foreign shell
] {
    let script_contents = $in;
    let env_out = with-env { SCRIPT_TO_SOURCE: $script_contents } {
        ^$shell ...$arguments -c `
        env
        echo '<ENV_CAPTURE_EVAL_FENCE>'
        eval "$SCRIPT_TO_SOURCE"
        echo '<ENV_CAPTURE_EVAL_FENCE>'
        env -u _ -u _AST_FEATURES -u SHLVL` # Filter out known changing variables
    }
    | split row '<ENV_CAPTURE_EVAL_FENCE>'
    | {
        before: ($in | first | str trim | lines)
        after: ($in | last | str trim | lines)
    }

    # Unfortunate Assumption:
    # No changed env var contains newlines (not cleanly parseable)
    $env_out.after
    | where { |line| $line not-in $env_out.before } # Only get changed lines
    | parse "{key}={value}"

Title: Working with Foreign Shell Scripts in Nushell
Summary
Nushell faces challenges when interacting with shell scripts designed for other shells like Bash or Zsh due to syntax incompatibilities. The chapter discusses workarounds such as parsing the script as a string, which is suitable for simple cases but becomes complex with advanced features like variable expansion. Another solution is using the third-party plugin 'bash-env' to import environment variables from Bash scripts, though it's not officially supported. The chapter also provides a complex approach to capture the changes made to the environment by running the script in its intended shell and then extracting the modified variables.