Home Explore Blog CI



nushell

2nd chunk of `cookbook/foreign_shell_scripts.md`
f4c4663cabfec76f73e43642ecb024a0518bb0932c5a0e5b0000000100000d33
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}"
    | transpose --header-row --as-record
}
```

Usage, e.g. in `env.nu`:

```nu
# Default usage, running the script with `/bin/sh`
load-env (open script.sh | capture-foreign-env)

# Running a different shell's script
# fish might be elsewhere on your system, if it's in the PATH, `fish` is enough
load-env (open script.fish | capture-foreign-env --shell /usr/local/bin/fish)
```

The command runs a foreign shell script and captures the changed environment
variables after running the script. This is done by parsing output of the `env`
command available on unix-like systems. The shell to execute can be specified
and configured using the `--shell` and `--arguments` parameters, the command has
been tested using sh (-> bash), bash, zsh, fish, ksh, and dash.

::: warning
A caveat for this approach is that it requires all changed environment variables
not to include newline characters, as the UNIX `env` output is not cleanly
parseable in that case.

Also beware that directly passing the output of `capture-foreign-env` to
`load-env` can result in changed variables like `PATH` to become strings again,
even if they have been converted to a list before.
:::

### Detailed Explanation of `capture-foreign-env`

Let's have a look at the command's signature first:

```nu
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;
    # ...
}
```

We're declaring a custom command that takes two optional flags:

- `--shell` to specify a shell to run the script in, (e.g. `bash`)
- `--arguments` to parse further command line arguments to that shell.

The actual script is not mentioned here, because it is read using the special
`$in` variable that represents anything passed to Standard Input (`stdin`), e.g.
via a pipe.

The shell is set to `/bin/sh` by default, because this is often considered the
"default" POSIX-compatible shell of UNIX-like systems, e.g. macOS or Linux. It
is often not running the original Bourne shell (`sh`), but linking to a
different shell, like `bash`, with some compatibility flags turned on.

As such, many "generic" shell scripts to source are compatible with the system'

Title: Implementation and Usage of the `capture-foreign-env` Command
Summary
The `capture-foreign-env` command is defined to execute foreign shell scripts and capture changes to environment variables. It takes the script content via stdin, specifies the shell using the `--shell` flag (defaulting to `/bin/sh`), and accepts additional shell arguments with `--arguments`. It captures the environment before and after script execution using the `env` command, identifies changed variables, and parses them. The command is demonstrated with usage examples for different shells like fish. Limitations include issues parsing newline characters in environment variables and potential type conversion problems when directly using the output with `load-env`.