NIX_PATH and polyglot shebang

I have found a working example using nix-shell shebangs for multiple programming languages:

#! /usr/bin/env nix-shell
#! nix-shell -I nixpkgs=http://nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz
echo "hello from bash";

#! nix-shell -p python -i python
python <<END_OF_PYTHON

import sys

print ("hello from python")

sys.exit(0)
END_OF_PYTHON

#! nix-shell -i ruby -p ruby
ruby <<END_OF_RUBY
puts "hello from ruby"
exit
END_OF_RUBY

#! nix-shell -i bash
echo "goodbye from bash!";

Just a template but you get the general idea.

Basically using here docs to breakout of language one and also re/using the according shebang (i. e. see near bottom in the example code) to switch or return to language (one)

EDIT: Deleted that part as I am unsure about this and also unnecessary information. I guess with languages not using # as comment marker you’d have to comment out certain sections in this example (shebang lines or continuations again should suffice) according to their comment token scheme (i. e. erlang smth like -define(HIDE_SHEBANG, <<" ...">>). )

This isn’t doing what you think it is (but I don’t mean to discourage you from playing with it; it’s a good way to learn, and I learned a few things I didn’t know about the behavior looking at this myself.)

$ cat ~/hmm3
Fri Apr 16 2021 15:16:41 --> 
#! /usr/bin/env nix-shell
#! nix-shell --pure
#! nix-shell
echo '
	note that (by file order) we have not added
	which or jq yet
'
which -a jq
#! nix-shell -p which
#! nix-shell -p jq
#! nix-shell -i python
echo definitely not python
echo in fact, python is not even on the path
type -a python
#! nix-shell -i bash
echo '
	because nix is processing all of these
	before it runs the file
	you can only have one -i flag (the last)
	but -p flags are additive
'
Fri Apr 16 2021 15:16:41 (15.99ms)

$ ~/hmm3
Fri Apr 16 2021 15:16:43 --> 
warning: warning: Nix search path entry '/nix/var/nix/profiles/per-user/root/channels' does not exist, ignoring

	note that (by file order) we have not added
	which or jq yet

/nix/store/536pqss7jciw91z6db73z5pxjach5098-jq-1.6-bin/bin/jq
definitely not python
in fact, python is not even on the path
/Users/abathur/hmm3: line 14: type: python: not found

	because nix is processing all of these
	before it runs the file
	you can only have one -i flag (the last)
	but -p flags are additive

Fri Apr 16 2021 15:16:44 (799ms)

By the time everything is getting added up and run, you’re running something like:

$ cat ~/hmm4
Fri Apr 16 2021 15:16:48 --> 
#! /usr/bin/env nix-shell
#! nix-shell -i bash -p python ruby
echo "hello from bash";

python <<END_OF_PYTHON
import sys

print ("hello from python")

sys.exit(0)
END_OF_PYTHON

ruby <<END_OF_RUBY
puts "hello from ruby"
exit
END_OF_RUBY

echo "goodbye from bash!";
Fri Apr 16 2021 15:16:48 (9.884ms)

$ ~/hmm4
Fri Apr 16 2021 15:16:54 --> 
warning: warning: Nix search path entry '/nix/var/nix/profiles/per-user/root/channels' does not exist, ignoring
hello from bash
hello from python
hello from ruby
goodbye from bash!
Fri Apr 16 2021 15:16:55 (979ms)
2 Likes

Thanks, I wondered especially about the -i part myself (all #!nix-shell are merged into just one) and the example you have given is excellent.

I’ll have to play further with this when on my machine again.

EDIT: Regarding the pattern in principle again.
@abathur already put all necessary information into one working example above. I cannot mark this a solution as my post was in the Howto-category. Should I rather edit the OP then ?

  • Use nix-shell shebang lines as needed as they are merged before script execution itself
  • Merging works with accumulation of any given dependencies (-p) plus selection of last found interpreter (-i)
  • Any other nix-shell options are possible with their cardinality kept in the merge result, i. e. the -I parameter can occur multiple times (see man nix-shell)
  • Using heredocs to breakout of language one, assumed language one supports interactive reading of those heredocs*
  • For each breakout language block remember to call the exit primtive in that language

(* Not saying “supports heredocs” here bc heredocs mostly are just syntactic sugar language one provides)

That’s it basically.

PS. Regarding a language such as Erlang using escript, the possibilities seem somewhat limited as escript supports no eval parameter or interaction evaluation and with i. e. erl things are different again. But that’s out of scope of nix in general and out of my knowledge.