The 'guideline' "To make scripts portable, use #!/usr/bin/env <cmd ...> rather than #!/path/to/cmd <...>." is bad advice. The correct solution to portability is to modify the scripts to contain the correct path (/bin/zsh, /usr/bin/zsh, /usr/local/bin/zsh) at installation time, and there is example code as part of the SUS standard for how to do this.
–
Random832May 2 '13 at 17:59

1

@Random832, can you provide link to SUS standard, never heard of that before.
–
slm♦May 2 '13 at 18:03

@slm sure - SUS stands for "single unix specification", the old name for the Open Group Base Specification that I linked in my answer.
–
Random832May 2 '13 at 18:04

1

@kjo Right. Those attacks are against scripts that run with elevated privileges. You can't usefully use env here anyway because you need to grab the interpreter from a well-known location (it's ok, but not very useful, to get it from $PATH if that is set by sudo).
–
GillesMay 2 '13 at 23:21

2 Answers
2

Linux (you mentioned "only under Ubuntu" but the only OS you mentioned it working under was Darwin) does not support passing multiple arguments to a 'shebang' interpreter. It passes the entire string (in your case, "zsh -") as a single argument.

I think the answer to your question is basically "no". The shebang mechanism just isn't that flexible.

The #! line only lets you specify a command to execute, and (optionally) a single argument to that command. The name of the script is passed as another argument. So if foo.zsh starts with:

#!/usr/bin/env zsh

the running foo.zsh is equivalent to running /usr/bin/env zsh foo.zsh.

If you specified the path to zsh directly, you could pass an additional argument:

#!/usr/bin/zsh -

But with the #!/usr/bin/env hack, zshis the additional argument.

Apparently this behavior varies from system to system, as you've seen. BTW, I'd check to make sure that the - isn't being ignored on Ubuntu.

If you can rely on zsh being in a consistent location on all the systems you care about, you can use #!/usr/bin/zsh - (ignoring your first guideline).

If you can't, then you can either create a symlink to zsh in a consistent location on each system, or you can modify the #! line in your script as you install it. (I've done this myself for Perl scripts, back in the days when Perl wasn't always /usr/bin/perl.)

Or you can write a wrapper that, when invoked with no arguments, invokes zsh with a - argument, and change your shebang to:

#!/usr/bin/env zsh-wrapper

which only requires zsh-wrapper to be somewhere in $PATH, not necessarily in a consistent location.