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'