How it works
Add the input
No nixpkgs of its own — nixsh takes pkgs from your evaluation, so it never adds a second nixpkgs to your closure.
Import a backend
homeModules.nixsh writes config. nixosModules.nixsh also installs the shells, because on NixOS the system is nix. systemManagerModules.nixsh publishes package names for a foreign distro's own reconciler.
Declare once
Enable the shells you want, set the shared environment, and add per-shell config where the shells genuinely differ.
Features
One environment, three syntaxes
Environment variables and PATH are declared once. fish gets set -gx and fish_add_path; bash and zsh get export. It is the only layer that genuinely translates, so it is the only layer that is shared.
Aliases stay per-shell, on purpose
fish is not POSIX. A generic alias DSL rendering to three syntaxes would render to three syntaxes badly, so aliases, functions and completions are declared per shell and never translated. The restraint is the design.
The binary stays the system's
home-manager's programs.fish.enable installs its own fish, which on a foreign distro lands ahead of /usr/bin/fish — so your login shell and your interactive shell become different builds. nixsh writes config only and never enables it.
Architecture
nixsh.environment.variables.EDITOR = "hx"
|
+-----------+-----------+
| | |
fish bash zsh
| | |
set -gx export export
| | |
conf.d/ initExtra initExtra
00-nixsh.fish
binary: /usr/bin/fish <- the system's
(never a second copy on PATH)
Shared where it translates
The environment layer is the only thing abstracted, because the only difference is the export keyword. Everything else is passed through verbatim in the shell's own language.
Config in nix, binaries in the system
On NixOS the system is nix, so the shells are installed. Anywhere else they come from the distro and nixsh writes config only — one shell per machine, not two.
Install
Foreign distro (Arch, CachyOS)
nixarch.packages.pacman = config.nixsh.archPackages;
Build from source
git clone https://github.com/julian-corbet/nixsh
cd nixsh
make install
Nix with flakes enabled. No other inputs — nixsh pulls no nixpkgs of its own.
Commands
nixsh.fish.enable = true; | Config for fish, written as a conf.d drop-in so it composes with whatever already owns config.fish. |
nixsh.environment.variables.EDITOR = "hx"; | Lands in every enabled shell at once, each in its own syntax. |
nixsh.fish.universalVariables.fish_greeting = ""; | fish owns fish_variables itself, so universal variables are applied idempotently at startup — the one piece of fish state a config file cannot declare. |