6

Is it possible to set environment variables while using nix-shell which gets invoked via a shebang? For example, I'm using

#! /usr/bin/env nix-shell
#! nix-shell -i runghc --pure

and would like to set an environment variable like FOO=bar. Note, FOO is not necessarily defined by the surrounding environment. Thus --keep FOO is not an option.

Max Maier
  • 985
  • 6
  • 16

2 Answers2

3

Yes, it's possible to set environment variables when utilizing nix with shebang.

nix as main language in a single file with bash shebang

#! /usr/bin/env -S bash -c 'nix-shell --pure $0'

with import <nixpkgs> {};
let
  haskellCode = ''
    import System.Environment (getEnv)

    main = do
      getEnv "VIA_DRV_ATTR" >>= print
      getEnv "VIA_SHELL_HOOK" >>= print
  '';
in
mkShell {
  buildInputs = [ ghc ];
  shellHook = ''
    export VIA_SHELL_HOOK="VIA_SHELL_HOOK works"
    exec runghc <<< '${haskellCode}'
  '';
  VIA_DRV_ATTR = "VIA_DRV_ATTR works";
}
$ ./test.nix 
"VIA_DRV_ATTR works"
"VIA_SHELL_HOOK works

The drawback is that haskell code gets embedded in a nix string.

haskell (or any other) as main language in a single file with nix shebang

#! /usr/bin/env nix-shell
#! nix-shell -i runghc --pure -E "with import <nixpkgs> {}; mkShell { buildInputs = [ ghc ]; VIA_DRV_ATTR = \"VIA_DRV_ATTR works\"; shellHook = ''export VIA_SHELL_HOOK=\"VIA_SHELL_HOOK works\"''; }"

import System.Environment (getEnv)

main = do
  getEnv "VIA_DRV_ATTR" >>= print
  getEnv "VIA_SHELL_HOOK" >>= print
$ ./test.hs
"VIA_DRV_ATTR works"
"VIA_SHELL_HOOK works"

The drawback is the lengthy shebang nix-shell line with escaped quotation marks. There doesn't seem to exist a way to break it into several lines.

both nix and another language in a single file with shebang?

Maybe it's possible to compose 2 language blocks one after another and write some generator script in shebang line in the manner of combining bash and haskell.

haskell (or any other) in a file with nix shebang with accompanying shell.nix file

AleXoundOS
  • 341
  • 1
  • 4
  • 13
2

Looks like you're using an accompanying shell.nix file. You could simply define your environment variable as a derivation attribute there.

shell.nix

pkgs.mkShell {
  buildInputs = [ pkgs.ghc ];
  shellHook = ''
    VIA_SHELL_HOOK=it\ works   # no it doesn't
  '';
  VIA_DRV_ATTR = "working";
}

Example executable script

#!/usr/bin/env nix-shell
#!nix-shell -i runghc --pure

import System.Environment(getEnv)

main = do
  getEnv "VIA_DRV_ATTR" >>= print
  getEnv "VIA_SHELL_HOOK" >>= print

Output:

"working"
hello: VIA_SHELL_HOOK: getEnv: does not exist (no environment variable)

(Nix 2.3.4, Nixpkgs 20.03)

Robert Hensing
  • 6,708
  • 18
  • 23
  • I'm not using an accompanying shell.nix file. However, I wasn't even aware that this is possible while using a shebang. So your solution works perfectly well for me. Thanks! – Max Maier Jun 08 '20 at 17:29
  • I'm not aware of a way to do make the `nix-shell` 'interpreter' do it without a `shell.nix`. In such a case you may be able to resort to setting the environment variable at the start of the script or the start of `main`. If you need to set a variable before the real interpreter runs, `shell.nix` is currently the only option. – Robert Hensing Jun 09 '20 at 08:47
  • I'm already experimenting with `shell.nix` :-) Thanks for pointing it out! – Max Maier Jun 09 '20 at 16:12