---
title: 🌀 Bin Gem Node
description: Annex - Bin Gem Node documentation.
canonical_url: https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node
markdown_url: https://wiki.zshell.dev/ecosystem/annexes/bin-gem-node/index.md
locale: en
source_path: ecosystem/annexes/1_bin_gem_node.mdx
---

<!-- Generated from the canonical Z-Shell Wiki build. The linked human-facing page remains the editorial source of truth. -->

# 🌀 Bin Gem Node

An annex provides the following functionality:

1.  Run programs and scripts without adding anything to `$PATH`,
2.  Install: [Ruby Gems](https://github.com/rubygems/rubygems), [Node](https://github.com/npm/cli), and [Python](https://python.org/) modules, with automatically set:
    -   [$GEM\_HOME](https://guides.rubygems.org/command-reference/#gem-environment)
    -   [$NODE\_PATH](https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders)
    -   [$VIRTUALENV](https://docs.python.org/3/tutorial/venv.html)
3.  Run programs, scripts, and functions with automatic `cd` into the plugin or snippet directory, plus also with automatic standard output & standard error redirecting.
4.  Source scripts through an automatically created function with the above `$GEM_HOME`, `$NODE_PATH`, `$VIRTUALENV`, and `cd` features available,
5.  Create the so-called `shims` known from [rbenv](https://github.com/rbenv/rbenv) – the same feature as the first item of this enumeration – of running a program without adding anything to `$PATH` with all of the above features, however through an automatic **script** created in `$ZPFX/bin`, not a **function** (the first item uses a function-based mechanism),
6.  Automatic updates of Ruby gems and Node modules during regular plugin and snippet updates with `zi update …`.

The [sbin](#sbin-1) ice-modifier that creates forwarder-scripts instead of forwarder-functions created by the [fbin](#fbin-2) ice-modifier turned out to be the proper, best method for exposing binary programs and scripts. This way there is no need to add anything to `$PATH` – `z-a-bin-gem-node` will automatically create a function that will wrap the binary and provide it on the command line as if it was being placed in the `$PATH`.

As previously mentioned, the function can automatically export `$GEM_HOME`, `$NODE_PATH`, `$VIRTUALENV` shell variables and also automatically cd into the plugin or snippet directory right before executing the binary and then cd back to the original directory after the execution is finished. As previously mentioned, instead of the function an automatically created script – the so-called `shim` – can be used for the same purpose and with the same functionality, so that the command is accessible practically fully normally – not only in the live Zsh session, only within which the functions created by [fbin](#fbin-2) exist, but also from any Zsh script.

Suppose that we want to install the `junegunn/fzf` plugin from GitHub Releases, which contains only a single file – the `fzf` binary for the selected architecture. It is possible to do it in the standard way – by adding the plugin's directory to the `$PATH`.

```zi
zi ice as'program' from'gh-r'
zi load junegunn/fzf
```

After this command, the `$PATH` variable will contain e.g.:

print $PATH

```zsh
/home/sall/.zi/plugins/junegunn---fzf:/bin:/usr/bin:/usr/sbin:/sbin
```

For many such programs loaded as plugins, the PATH can become quite cluttered. I've had 26 entries before switching to `z-a-bin-gem-node`. To solve this, load with the use of [sbin](#sbin-1) ice-modifier provided and handled by `z-a-bin-gem-node`:

```zi
zi ice as'program' from'gh-r' sbin'fzf'
zi load junegunn/fzf
```

The `$PATH` will remain unchanged and a forwarder-script of `fzf` shim will be created in `$ZPFX/bin` (`${ZI[HOME_DIR]}/polaris/bin` by default), which is being already added to the `$PATH` by Zi when it is being sourced:

cat $ZPFX/bin/fzf

```zsh
#!/usr/bin/env zsh

function fzf {
  local bindir="/home/sall/.zi/plugins/junegunn---fzf"
  "$bindir"/"fzf" "$@"
}

fzf "$@"
```

Running the script will forward the call to the program accessed through an embedded path to it. Thus, no `$PATH` changes are needed.

<a id="sbin"></a>
<a id="fbin"></a>
<a id="gem"></a>
<a id="node"></a>
<a id="pip"></a>
<a id="fmod"></a>
<a id="fsrc"></a>
<a id="ferc"></a>

<table><thead><tr><th>Ice modifier</th><th>Description</th></tr></thead><tbody><tr id="sbin"><td><a href="#sbin-1">sbin</a></td><td>Creates <code>shims</code> for binaries and scripts.</td></tr><tr id="fbin"><td><a href="#fbin-2">fbin</a></td><td>Creates functions for binaries and scripts.</td></tr><tr id="gem"><td><a href="#gem-3">gem</a></td><td>Installs and updates gems + creates functions for gems binaries.</td></tr><tr id="node"><td><a href="#node-4">node</a></td><td>Installs and updates node_modules + creates functions for binaries of the modules.</td></tr><tr id="pip"><td><a href="#pip-5">pip</a></td><td>Installs and updates python packages into a <code>virtualenv</code> + creates functions for binaries of the packages.</td></tr><tr id="fmod"><td><a href="#fmod-6">fmod</a></td><td>Creates wrapping functions for other functions.</td></tr><tr id="fsrc"><td><a href="#fsrc-7">fsrc</a></td><td>Creates functions that source given scripts.</td></tr><tr id="ferc"><td><a href="#ferc-8">ferc</a></td><td>The same as <a href="#fsrc-7">fsrc</a>, but using an alternate script-loading method.</td></tr></tbody></table>

Function wrappers for binaries, scripts, gems, node\_modules, python packages, etc:

<a id="g"></a>
<a id="n"></a>
<a id="p"></a>
<a id="c"></a>
<a id="N"></a>
<a id="E"></a>
<a id="O"></a>

<table><thead><tr><th>Flag</th><th>Description</th></tr></thead><tbody><tr id="g"><td><code>g</code></td><td>Set <code>$GEM_HOME</code> variable to <code>{plugin-dir}</code>.</td></tr><tr id="n"><td><code>n</code></td><td>Set <code>$NODE_PATH</code> variable to <code>{plugin-dir}/node_modules</code>.</td></tr><tr id="p"><td><code>p</code></td><td>Set <code>$VIRTUALENV</code> variable to <code>{plugin-dir}/venv</code>.</td></tr><tr id="c"><td><code>c</code></td><td><code>cd</code> to the plugin's directory before running the program and then cd back after it has been run.</td></tr><tr id="N"><td><code>N</code></td><td>Append <code>&amp;&gt;/dev/null</code> to the call of the binary, i.e. redirect both standard output and standard error to <code>/dev/null</code>.</td></tr><tr id="E"><td><code>E</code></td><td>Append <code>2&gt;/dev/null</code> to the call of the binary, i.e. redirect standard error to <code>/dev/null</code>.</td></tr><tr id="O"><td><code>O</code></td><td>Append <code>&gt;/dev/null</code> to the call of the binary, i.e. redirect standard output to <code>/dev/null</code>.</td></tr></tbody></table>

View all currently registered:

-   ice-modifiers: `zi icemods`
-   subcommand: `zi subcmds`

<a id="sbin-1"></a>

## `SBIN'…'`

```zi
sbin'[{g|n|c|N|E|O}:]{path-to-binary}[ -> {name-of-the-script}]; …'
```

Creates the so-called `shim` known from `rbenv` – a wrapper script that forwards the call to the actual binary. The script is created always under the same, standard, and single `$PATH` entry: `$ZPFX/bin` (which is `${ZI[HOME_DIR]}/polaris/bin` by default). The flags have the same meaning as with `fbin'…'` ice.

```zi
zi ice as'program' from'gh-r' sbin'fzf'
zi load junegunn/fzf
```

cat $ZPFX/bin/fzf

```zsh
#!/usr/bin/env zsh

function fzf {
  local bindir="/home/sall/.zi/plugins/junegunn---fzf"
  local -xU PATH="$bindir":"$PATH"
  "$bindir"/"fzf" "$@"
}

fzf "$@"
```

note

-   as'program' (an alias: as'command') - used for the plugin to be added to $PATH when a plugin is not a file for sourcing.

The [sbin](#sbin-1) ice-modifier can be empty, it will then try to create the shim for the trailing component of the [id-as](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#id-as) ice, e.g.:

-   `id_as'exts/git-my'` → it'll check if a file `git-my` exists and if yes, will create the function `git-my`.
-   `paulirish/git-open` → it'll check if a file `git-open` exists and if yes, will create the function `git-open`.

The same trailing component would be set for the snippet URL, for any alphabetically first and executable file.

<a id="fbin-2"></a>

## `FBIN'…'`

```zi
fbin'[{g|n|c|N|E|O}:]{path-to-binary}[ -> {name-of-the-function}]; …'
```

Creates a wrapper function of the name the same as the last segment of the path or as `{name-of-the-function}`.

```zi
zi ice from"gh-r" fbin"g:fzf -> myfzf" nocompile
zi load junegunn/fzf
```

which myfzf

```zsh
myfzf () {
  local bindir="/home/sall/.zi/plugins/junegunn---fzf"
  local -x GEM_HOME="/home/sall/.zi/plugins/junegunn---fzf"
  local -xU PATH="/home/sall/.zi/plugins/junegunn---fzf"/bin:"$bindir":"$PATH"
  "$bindir"/"fzf" "$@"
}
```

note

-   `nocompile` ice-modifier is used to skip file compilation when it is not required.

<a id="gem-3"></a>

## `GEM'…'`

```zi
gem'{gem-name}; …'
gem'[{path-to-binary} <-] !{gem-name} [-> {name-of-the-function}]; …'
```

Installs the gem of name `{gem-name}` with `$GEM_HOME` set to the plugin's or snippet's directory. In other words, the gem and its dependencies will be installed locally in that directory. In the second form, it also creates a wrapper function identical to the one created with `fbin'…'` ice.

```zi
zi ice gem'!asciidoctor' id-as'asciidoctor' nocompile
zi load z-shell/0
```

which asciidoctor

```zsh
asciidoctor () {
  local bindir="/home/sall/.zi/plugins/asciidoctor/bin"
  local -x GEM_HOME="/home/sall/.zi/plugins/asciidoctor"
  local -xU PATH="/home/sall/.zi/plugins/asciidoctor"/bin:"$bindir":"$PATH"
  "$bindir"/"asciidoctor" "$@"
}
```

note

-   `z-shell/0` - an empty repository to aid Zi's hooks, in this case, used to store the `asciidoctor` gem.
-   `id-as'asciidoctor'` - used to assign a name instead of the `z-shell/0`.
-   `nocompile` - used to skip file compilation when it is not required.

<a id="node-4"></a>

## `NODE'…'`

```zi
node'{node-module}; …'
node'[{path-to-binary} <-] !{node-module} [-> {name-of-the-function}]; …'
```

Installs the node module of name `{node-module}` inside the plugin's or snippet's directory. In the second form, it also creates a wrapper function identical to the one created with `fbin'…'` ice.

```zi
zi ice node'remark <- !remark-cli -> remark; remark-man' id-as'remark' nocompile
zi load z-shell/0
```

which remark

```zsh
remark () {
  local bindir="/home/sall/.zi/plugins/remark/node_modules/.bin"
  local -x NODE_PATH="/home/sall/.zi/plugins/remark"/node_modules
  local -xU PATH="/home/sall/.zi/plugins/remark"/node_modules/.bin:"$bindir":"$PATH"
  "$bindir"/"remark" "$@"
}
```

In this case, the name of the binary program provided by the node module is different from its name, hence the second form with the `b <- a -> c` syntax has been used.

note

-   `z-shell/0` - an empty repository to aid Zi's hooks, in this case, used to store the `remark` Node module.
-   `id-as'remark'` - used to assign a name instead of the `z-shell/0`.
-   `nocompile` - used to skip file compilation when it is not required.

<a id="pip-5"></a>

## `PIP'…'`

```zi
pip'{pip-package}; …'
pip'[{path-to-binary} <-] !{pip-package} [-> {name-of-the-function}]; …'
```

Installs the node module of name `{pip-package}` inside the plugin's or snippet's directory. In the second form, it also creates a wrapper function identical to the one created with `fbin'…'` ice.

```zi
zi ice pip'youtube-dl <- !youtube-dl -> youtube-dl' id-as'youtube-dl' nocompile
zi load z-shell/0
```

which youtube-dl

```zsh
youtube-dl () {
  local bindir="/home/sall/.zi/plugins/youtube-dl/venv/bin"
  local -x VIRTUALENV="/home/sall/.zi/plugins/youtube-dl"/venv
  local -xU PATH="/home/sall/.zi/plugins/youtube-dl"/venv/bin:"$bindir":"$PATH"
  "$bindir"/"youtube-dl" "$@"
}
```

note

-   `z-shell/0` - an empty repository to aid Zi's hooks, in this case, used to store the `youtube-dl` pip package.
-   `id-as'youtube-dl'` - used to assign a name instead of the `z-shell/0`.
-   `nocompile` - used to skip file compilation when it is not required.

<a id="fmod-6"></a>

## `FMOD'…'`

```zi
fmod'[{g|n|c|N|E|O}:]{function-name}; …'
fmod'[{g|n|c|N|E|O}:]{function-name} -> {wrapping-function-name}; …'
```

It wraps the given function with the ability to set `$GEM_HOME`, etc. – the meaning of the `g`, `n`, and `c` flags is the same as in the `fbin'…'` ice.

Example:

```zi
myfunc() { pwd; ls -1 }; zi ice fmod'cgn:myfunc' id-as'myfunc' nocompile
zi load z-shell/0
```

which myfunc

```zsh
myfunc () {
  local -x GEM_HOME="/home/sall/.zi/plugins/myfunc"
  local -x NODE_PATH="/home/sall/.zi/plugins/myfunc"/node_modules
  local oldpwd="/home/sall"
  () {
    setopt local_options no_auto_pushd
    builtin cd -q "/home/sall/.zi/plugins/myfunc"
  }
  "myfunc--za-bgn-orig" "$@"
  () {
    builtin setopt local_options no_auto_pushd
    builtin cd -q "$oldpwd"
  }
}
```

myfun

```zsh
/home/sall/.zi/plugins/z-shell---0
docs/
LICENSE
README.md
```

note

-   `z-shell/0` - an empty repository to aid Zi's hooks, in this case, used to store the `myfunc` function files.
-   `id-as'myfunc'` - used to assign a name instead of the `z-shell/0`.
-   `nocompile` - used to skip file compilation when it is not required.

<a id="fsrc-7"></a>

## `FSRC'…'`

```zi
fsrc'[{g|n|c|N|E|O}:]{path-to-script}[ -> {name-of-the-function}]; …'
```

<a id="ferc-8"></a>

## `FERC'…'`

```zi
ferc'[{g|n|c|N|E|O}:]{path-to-script}[ -> {name-of-the-function}]; …'
```

Creates a wrapper function that at each invocation sources the given file. The second ice, `FERC'…'` works the same with the single difference that it uses `eval "$(<{path-to-script})"` instead of `source "{path-to-script}"` to load the script.

```zi
zi ice fsrc"myscript -> myfunc" ferc"myscript" nocompile
zi load z-shell/0
```

which myfunc

```zsh
myfunc () {
  local bindir="/home/sall/.zi/plugins/z-shell---0"
  local -xU PATH="$bindir":"$PATH"
  () {
    source "$bindir"/"myscript"
  } "$@"
}
```

which myscript

```zsh
myscript () {
  local bindir="/home/sall/.zi/plugins/z-shell---0"
  local -xU PATH="$bindir":"$PATH"
  () {
    eval "$(<"$bindir"/"myscript")"
  } "$@"
}
```

note

-   `nocompile` ice-modifier is used to skip file compilation when it is not required.

The ices can be empty as the trailing component will be assigned with [id-as](https://wiki.zshell.dev/docs/guides/syntax/standard/index.md#id-as) ice-modifier the same way as described in the [sbin](#sbin-1).

<a id="shim-list"></a>

## `shim-list`

An annex provides a subcommand – `shim-list` for shims currently stored in `$ZPFX/bin` management:

Available flags are:

```zi
zi shim-list [ -t | -i | -o | -s | -h ]
```

<table><thead><tr><th>Flag</th><th>Description</th></tr></thead><tbody><tr><td><code>-t</code> <code>--this-dir</code></td><td>Instructs Zi to look for shims in the current directory instead of <code>$ZPFX/bin</code>.</td></tr><tr><td><code>-i</code> <code>--from-ices</code></td><td>Normally the code looks for the shim files by examining their contents (more info <sup><a href="#user-content-fn-1" id="user-content-fnref-1">1</a></sup>).</td></tr><tr><td><code>-o</code> <code>--one-line</code></td><td>Display the list of shim files without line breaks, in a single line, after spaces.</td></tr><tr><td><code>-s</code> <code>--short</code></td><td>Don't show the plugin/snippet that the shim belongs to.</td></tr><tr><td><code>-h</code> <code>--help</code></td><td>Shows usage information.</td></tr></tbody></table>

<a id="cygwin-support"></a>

## Cygwin support

The [sbin](#sbin-1) ice-modifier has an explicit Cygwin support – it creates additional, **extra shim files** – Windows batch scripts that allow running the shielded applications from e.g.: Windows run dialog – if the `${ZI[HOME_DIR]}/polaris/bin` directory is being added to the Windows `PATH` environment variable, for example (it is a good idea to do so, IMHO). The Windows shims have the same name as the standard ones (which are also being created, normally) plus the `.cmd` extension. You can test the feature by e.g.: installing Firefox from the Zi package via:

```zi
zi pack=bgn for firefox
```

<a id="install-bin-gem-node"></a>

## Install bin-gem-node

Source

-   [
    
    z-shell/z-a-bin-gem-node
    
    ](https://github.com/z-shell/z-a-bin-gem-node)

-   Default
-   Unscoped

Add the following snippet in the `.zshrc` file:

```zi
zi light z-shell/z-a-bin-gem-node
```

Add the following snippet in the `.zshrc` file to install using the [unscope](https://wiki.zshell.dev/ecosystem/annexes/unscope/index.md) annex:

```zi
zi light z-shell/z-a-unscope bgn
```

This will register the [shim-list](#shim-list) subcommand and following ice-modifiers:

<a id="footnote-label"></a>

## Footnotes

1.  <a id="user-content-fn-1"></a>
    
    shims created by the `bin-gem-node` annex have a fixed structure, this option instructs Zi to show the list of shims that results from the [sbin](#sbin-1) ice-modifier of the loaded plugins. If a plugin for example has `sbin'git-open'`, means that such shim has already been created. [↩](#user-content-fnref-1)
