36.4. Making #! Search the PATH
As Section 36.3 explains, you can use #!/path/name to run a script with the interpreter located at /path/name in the filesystem. The problem comes if a new version of the interpreter is installed somewhere else or if you run the script on another system that has a different location. It's usually not a problem for Bourne shell programmers: /bin/sh exists on every Unix-type system I've seen. But some newer shells -- and interpreters like Perl -- may be lurking almost anywhere (although this is becoming more and more standardized as Perl and other tools like it become part of standard Linux distributions and the like). If the interpreter isn't found, you'll probably get a cryptic message like scriptname: Command not found, where scriptname is the name of the script file.
The env command will search your PATH (Section 35.6) for an interpreter, then execute (exec (Section 24.2), replace itself) with the interpreter. If you want to try this, type env ls; env will find and run ls for you. This is pretty useless when you have a shell around to interpret your commands -- because the shell can do the same thing without getting env involved. But when the kernel interprets an executable file that starts with #!, there's no shell (yet!). That's where you can use env. For instance, to run your script with zsh, you could start its file with:
#!/usr/bin/env zsh ...zsh script here...
The kernel execs /usr/bin/env, then env finds and execs the zsh it found. Nice trick, eh? What do you think the problem is? (You have ten seconds... tick, tick, tick...) The catch is: if the env command isn't in /usr/bin on your system, this trick won't work. So it's not as portable as it might be, but it's still handy and probably still better than trying to specify the pathname of a less common interpreter like zsh.
Running an interpreter this way can also be a security problem. Someone's PATH might be wrong; for instance, it might execute some random command named zsh in the user's bin directory. An intruder could change the PATH to make the script use a completely different interpreter with the same name.
One more problem worth mentioning: you can't specify any options for the interpreter on the first line. Some shell options can be set later, as the script starts, with a command like set, shopt, and so on -- check the shell's manual page.
Finally, understand that using env like this pretty much erases any performance gains you may have achieved using the trick in the previous article.
--JP and SJC
Copyright © 2003 O'Reilly & Associates. All rights reserved.