These are part of the rituals of learning how a system works, in the same way interns get tripped up at first when they discover ^S will hang an xterm, until ^Q frees it. If you're aware of the history of it, it makes perfect sense. Unix has a personality, and in this case the kernel needs to decide what executable to run before any shell is involved, so it deliberately avoids the complexity of quoting rules.
I'd give this a try, works with any language:
#!/usr/bin/env -S "/path/with spaces/my interpreter" --flag1 --flag2
Only if my env didn't have -S support, I might consider a separate launch script like: #!/bin/sh
exec "/path/with spaces/my interpreter" "$0" "$@"
But most decent languages seems to have some way around the issue.Python
#!/bin/sh
""":"
exec "/path/with spaces/my interpreter" "$0" "$@"
":"""
# Python starts here
print("ok")
Ruby #!/bin/sh
exec "/path/with spaces/ruby" -x "$0" "$@"
#!ruby
puts "ok"
Node.js #!/bin/sh
/* 2>/dev/null
exec "/path/with spaces/node" "$0" "$@"
*/
console.log("ok");
Perl #!/bin/sh
exec "/path/with spaces/perl" -x "$0" "$@"
#!perl
print "ok\n";
Common Lisp (SBCL) / Scheme (e.g. Guile) #!/bin/sh
#|
exec "/path/with spaces/sbcl" --script "$0" "$@"
|#
(format t "ok~%")
C #!/bin/sh
#if 0
exec "/path/with spaces/tcc" -run "$0" "$@"
#endif
#include <stdio.h>
int main(int argc, char **argv)
{
puts("ok");
return 0;
}
Racket #!/bin/sh
#|
exec "/path/with spaces/racket" "$0" "$@"
|#
#lang racket
(displayln "ok")
Haskell #!/bin/sh
#if 0
exec "/path/with spaces/runghc" -cpp "$0" "$@"
#endif
main :: IO ()
main = putStrLn "ok"
Ocaml (needs bash process substitution) #!/usr/bin/env bash
exec "/path/with spaces/ocaml" -no-version /dev/fd/3 "$@" 3< <(tail -n +3 "$0")
print_endline "ok";;