`nix-shell --pure` nix-command equivalent

TL:DR: Is there nix-shell --pure equivalent in nix-commands (i.e nix develop or nix shell)?

Use-case: Declarative description of developer environment (and most of it’s setup) via nix derivation.

For minimal example:

$  cat /etc/os-release
BUG_REPORT_URL="https://github.com/NixOS/nixpkgs/issues"
BUILD_ID="22.05.20221030.26eb67a"
DOCUMENTATION_URL="https://nixos.org/learn.html"
HOME_URL="https://nixos.org/"
ID=nixos
LOGO="nix-snowflake"
NAME=NixOS
PRETTY_NAME="NixOS 22.05 (Quokka)"
SUPPORT_URL="https://nixos.org/community.html"
VERSION="22.05 (Quokka)"
VERSION_CODENAME=quokka
VERSION_ID="22.05"
$ nix --version
nix (Nix) 2.8.1
$ echo $PATH
/run/wrappers/bin:/home/<user_name>/.nix-profile/bin:/etc/profiles/per-user/<user_name>/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin
# dev.nix
{ nixpkgs ? import <nixpkgs> {} }:

nixpkgs.mkShell {
  name = "dev-shell";
  buildInput = with nixpkgs; [
    hello
  ];
  shellHook = ''
    echo "test";
  '';
}

Simplified usage transcript:

$ rg --version
ripgrep 13.0.0
$ hello
The program 'hello' is not in your PATH.
$ nix-shell --pure ./dev.nix
'test'
$ hello
Hello, world!
$ rg --version
The program 'rg' is not in your PATH.
$ echo $PATH
echo $PATH
/nix/store/pa9416c0x5qa4r2z3zq701qg8gyr0c73-bash-interactive-5.1-p16/bin:/nix/store/n1jgqr8xzjz9shn3ads5x07p8lqn5rqk-patchelf-0.14.5/bin:/nix/store/r7r10qvsqlnvbzjkjinvscjlahqbxifl-gcc-wrapper-11.3.0/bin:/nix/store/d7q0qfm12hb59wj63wyjs0hrdhmmapfz-gcc-11.3.0/bin:/nix/store/vkwlwmagq5i0if2f60ywrg2gxjf5xr9i-glibc-2.34-210-bin/bin:/nix/store/47n5hzqpahs7yv84ia6cxp3jg9ca8r86-coreutils-9.0/bin:/nix/store/10h6ymfb28wx6x8amj82h2sgw3ixrsb2-binutils-wrapper-2.38/bin:/nix/store/2b99rpx8zwdjjqkrvk7kqgn9mxhiidjy-binutils-2.38/bin:/nix/store/47n5hzqpahs7yv84ia6cxp3jg9ca8r86-coreutils-9.0/bin:/nix/store/6ib6hn9fq8mgkdq2nq5f7kz050p49rp2-findutils-4.9.0/bin:/nix/store/685c5dr4agkf7vx8ya7f1r9rd9qwg2ri-diffutils-3.8/bin:/nix/store/sppjn85p06m1il70kd05drg1j26cjxd3-gnused-4.8/bin:/nix/store/49vp3yp54fqliy7k8gvxsybd50l9a82f-gnugrep-3.7/bin:/nix/store/fr7vrxblkj327ypn3vhjwfhf19lddqqd-gawk-5.1.1/bin:/nix/store/5p3qyadsv163m7zvqssiw80zh6xfv2jv-gnutar-1.34/bin:/nix/store/2bwqikh67y1231ccb71gjfrggwjw066q-gzip-1.12/bin:/nix/store/wjf2554ffvap47vanabh9lk0dmj1q295-bzip2-1.0.6.0.2-bin/bin:/nix/store/2hvj24gaq4y32cyf0jp9sj01y00k7czy-gnumake-4.3/bin:/nix/store/0d3wgx8x6dxdb2cpnq105z23hah07z7l-bash-5.1-p16/bin:/nix/store/wa31fy0bgmq7p2gcvh5xyrr7m2v8i3s2-patch-2.7.6/bin:/nix/store/dr5shmim604rh50mmihwfic80k0wa3k0-xz-5.2.5-bin/bin

nix-shell --pure allows me not only to get rid of most of annoying environment variables, but also ensure that working environment (i.e. $PATH) is not populated with whatever user has in his default environment. I do not want binaries being propagated from outside of the nix derivation.

The newer versions of nix have nix develop and nix shell commands, which completely do not meet my requirements and what nix-shell --pure was doing.

For example:

# flake.nix
{
  inputs = {
    flake-utils.url = "github:numtide/flake-utils";
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.05";
  };
 outputs = { self, nixpkgs, flake-utils, ... }:
    flake-utils.lib.eachDefaultSystem (system: {
      "nixpkgs" = import nixpkgs { inherit system; };
      "devShell" = import ./dev.nix { nixpkgs = import nixpkgs { inherit system; }; };
    });
}

Simplified usage transcript:

$ rg --version
ripgrep 13.0.0
$ hello
The program 'hello' is not in your PATH.
$ nix develop -i
'test'
$ hello
Hello, world!
$ rg --version
ripgrep 13.0.0 #SIC! It is still in the $PATH
$ echo $PATH
echo $PATH
/nix/store/n845kl3915ri14zlkqq7wf3n3xabmgal-patchelf-0.14.5/bin:/nix/store/ykcrnkiicqg1pwls9kgnmf0hd9qjqp4x-gcc-wrapper-11.3.0/bin:/nix/store/65v2c245h5qa9mpc7dxhqkfjinl6phx0-gcc-11.3.0/bin:/nix/store/rvgp96bwfmgz4b163flszfaygmhx8wl4-glibc-2.34-210-bin/bin:/nix/store/qarssrazji0q9xp80xg8shsm2crckfr0-coreutils-9.0/bin:/nix/store/n5ypzvl7dijcg24isyngvw8fx0ri6hff-binutils-wrapper-2.38/bin:/nix/store/0q9hm42fapihzj1d64nxqmbml7fpb2d6-binutils-2.38/bin:/nix/store/qarssrazji0q9xp80xg8shsm2crckfr0-coreutils-9.0/bin:/nix/store/j25abvpcbappy74w23l8lfcz7gkrsjhy-findutils-4.9.0/bin:/nix/store/2kz1pihzg1jfif46mdm917xmj6r9xyz6-diffutils-3.8/bin:/nix/store/yyg26p5j2mrjwpkbk1djh4nxlsm2p4rw-gnused-4.8/bin:/nix/store/p7pqs5c4zfc4y977p626zch11msmmpj8-gnugrep-3.7/bin:/nix/store/pmh9q9k0g9s189v0iqrxpdp8j1g77gmd-gawk-5.1.1/bin:/nix/store/3fw5n1g3bb925hfll7fj7x94bd0q6k0r-gnutar-1.34/bin:/nix/store/nrvhb0yvawiqgrwbmbfmhjrmy934hhs5-gzip-1.12/bin:/nix/store/n7208v30hf3z4sz6127947vmzpfl46nb-bzip2-1.0.6.0.2-bin/bin:/nix/store/c5myz1zs9bsaq3y4s0rcxgkzb11irwra-gnumake-4.3/bin:/nix/store/9zm6br2ri10a0b71dll2wrim5bnhg6b6-bash-5.1-p16/bin:/nix/store/8636bkd1gg5s8675ipb9wf98wrc5mdpw-patch-2.7.6/bin:/nix/store/5ywhaxrasrp3pj72dnyy40mimx13fi0p-xz-5.2.5-bin/bin:/run/wrappers/bin:/.nix-profile/bin:/etc/profiles/per-user//bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin

I have been unable to figure out how to retain the behaviour of the nix-shell --pure in new nix-commands - is it even possible?
I know that I can use flake-compact to run nix-shell --pure on flake, but I feel like it is a huge omission to forgo this capability in nix-commands.

Edit #1: Added list of OS, nix versions and $PATH content

2 Likes

This doesn’t match my results:

tlater ~ $ rg --version
ripgrep 13.0.0
-SIMD -AVX (compiled)
+SIMD +AVX (runtime)
tlater ~ $ nix develop -i nixpkgs#python3
bash-5.1$ rg --version
rg: command not found

I’ve confirmed that with actual flake.nixes as well.

Is it perhaps bash loading some environment variables when it’s starting for you? Maybe bash needs to be invoked with --norc --noprofile or such. What does your $PATH actually contain?

In this particular case, I noticed that when you use NixOS and you install e.g. rg globally (environment.systemPackages) it will be still available in your dev shell (checked with silver-searcher :slight_smile: )

$ nix develop -i
test
bash-5.1$ ag

Usage: ag [FILE-TYPE] [OPTIONS] PATTERN [PATH]
...

And I can confirm that everything from environment.systemPackages leaks to the dev shell.

It does not for me. When I start a nix develop -i then /run/current-system/sw/bin will not be in PATH anymore.

$ nix develop -i nixpkgs\#hello -c printenv PATH | rg current-system
$ echo $?
1

This gets interesting :slight_smile: (/run/current-system/sw/bin in the end when run sequentially…):

$ nix develop -i nixpkgs\#hello
bash-5.1$ printenv PATH
/nix/store/7bdilcs9ygs5dwynn798f26060fggp7g-patchelf-0.15.0/bin:/nix/store/2bg235ac1s6cpxwr1pjagn6bj8q2425l-gcc-wrapper-11.3.0/bin:/nix/store/8xmmwmb6r6hc6f3cdh64mbi138p96vpg-gcc-11.3.0/bin:/nix/store/4xynblhic6sq4paq2ra4ql535yyvracs-glibc-2.35-163-bin/bin:/nix/store/xq4g38m5ppg78z7bzfxyy2s8ii693k74-coreutils-9.1/bin:/nix/store/bj1i5gr4l6p5pkcfyp48mni85d2ydz71-binutils-wrapper-2.39/bin:/nix/store/pmgnlnbygb95s4zc8sqhknz9sdz934pk-binutils-2.39/bin:/nix/store/xq4g38m5ppg78z7bzfxyy2s8ii693k74-coreutils-9.1/bin:/nix/store/1s3ifgirxw3x89b64a6sg6pxydgz8cr1-findutils-4.9.0/bin:/nix/store/1wf98fslhfph028xhkxdbp3q8y8rhr02-diffutils-3.8/bin:/nix/store/jvhh6i1647pn2f1kay9xk8b7i8acdjh1-gnused-4.8/bin:/nix/store/sqypvzmrsl8hil564xfk8n5w51gq47h0-gnugrep-3.7/bin:/nix/store/pq5rfrihdhhj6aqjh9ix8dycgp55ij8p-gawk-5.1.1/bin:/nix/store/m2jgkyyr88nmxil78c4jx1sh4xzw7y1v-gnutar-1.34/bin:/nix/store/acslph124hwcca3dkccd7ybl7wmc5j3p-gzip-1.12/bin:/nix/store/van5xy9axychdx2q7b4mv9apqhxfng3b-bzip2-1.0.8-bin/bin:/nix/store/25nm17pd42qmyczrkh80ya2hh9jvgp0g-gnumake-4.3/bin:/nix/store/y0g1mvsr6vygr61f9znljik9kl0x0inc-bash-5.1-p16/bin:/nix/store/xjk7y6cbxn8g54b0fh67rs1h69g62hnl-patch-2.7.6/bin:/nix/store/71lr8dndinlkiyjmdcgi8kq2xhjdchn6-xz-5.2.7-bin/bin:/nix/store/dxz4jr6gj2day09ribxiai0406p64nm0-file-5.43/bin:/run/wrappers/bin:/.nix-profile/bin:/etc/profiles/per-user//bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin
bash-5.1$ exit
$ nix develop -i nixpkgs\#hello -c printenv PATH
/nix/store/7bdilcs9ygs5dwynn798f26060fggp7g-patchelf-0.15.0/bin:/nix/store/2bg235ac1s6cpxwr1pjagn6bj8q2425l-gcc-wrapper-11.3.0/bin:/nix/store/8xmmwmb6r6hc6f3cdh64mbi138p96vpg-gcc-11.3.0/bin:/nix/store/4xynblhic6sq4paq2ra4ql535yyvracs-glibc-2.35-163-bin/bin:/nix/store/xq4g38m5ppg78z7bzfxyy2s8ii693k74-coreutils-9.1/bin:/nix/store/bj1i5gr4l6p5pkcfyp48mni85d2ydz71-binutils-wrapper-2.39/bin:/nix/store/pmgnlnbygb95s4zc8sqhknz9sdz934pk-binutils-2.39/bin:/nix/store/xq4g38m5ppg78z7bzfxyy2s8ii693k74-coreutils-9.1/bin:/nix/store/1s3ifgirxw3x89b64a6sg6pxydgz8cr1-findutils-4.9.0/bin:/nix/store/1wf98fslhfph028xhkxdbp3q8y8rhr02-diffutils-3.8/bin:/nix/store/jvhh6i1647pn2f1kay9xk8b7i8acdjh1-gnused-4.8/bin:/nix/store/sqypvzmrsl8hil564xfk8n5w51gq47h0-gnugrep-3.7/bin:/nix/store/pq5rfrihdhhj6aqjh9ix8dycgp55ij8p-gawk-5.1.1/bin:/nix/store/m2jgkyyr88nmxil78c4jx1sh4xzw7y1v-gnutar-1.34/bin:/nix/store/acslph124hwcca3dkccd7ybl7wmc5j3p-gzip-1.12/bin:/nix/store/van5xy9axychdx2q7b4mv9apqhxfng3b-bzip2-1.0.8-bin/bin:/nix/store/25nm17pd42qmyczrkh80ya2hh9jvgp0g-gnumake-4.3/bin:/nix/store/y0g1mvsr6vygr61f9znljik9kl0x0inc-bash-5.1-p16/bin:/nix/store/xjk7y6cbxn8g54b0fh67rs1h69g62hnl-patch-2.7.6/bin:/nix/store/71lr8dndinlkiyjmdcgi8kq2xhjdchn6-xz-5.2.7-bin/bin:/nix/store/dxz4jr6gj2day09ribxiai0406p64nm0-file-5.43/bin:/no-such-path

Oooof…
Yes, when not using -c I can reproduce…

And after I delete /etc/bashrc I can not reproduce anymore. So it is the impure fact that bash reads in its own configuration files.

3 Likes

Hi all,
Thanks for your pointers - I have updated the post to include my OS, nix version and $PATH.

Precisely as @kczulko has noticed, the differences seem to be with default profiles being appended at the end (i.e. :/.nix-profile/bin:/etc/profiles/per-user//bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin).

My follow-up question would then be: is that expected behaviour? Imho nix-shell --pure works better by default and I would consider contributing upstream to fix the nix develop

I think they reason why it works for nix-shell is, that it uses the bash derivation provided by the stdenv, while nix develop replaces that with bashInteractive for convinience reasons.

The problem with the former is that the prompt and some other niceties for humans do not work (correctly), as thats a stripped down bash optimized for running scripts and keeping the closure smaller.

2 Likes

weirdly enough if i do nix shell -i -c bash i get a strange incomplete PATH of “/.local/bin:/run/wrappers/bin:/.nix-profile/bin:/etc/profiles/per-user//bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin” but yes i have tested and using bash --norc --noprofile works to get a clean PATH

@AleksanderGondek
My colleague showed me this post
As a follow up - this is what works in my case (zsh):
nix develop -i -c $SHELL

1 Like