While at first shebangs may seem pretty obvious and well supported, there is a number of not-so-well-known portability issues affecting them. Only during my recent development work, I have hit more than one of them. For this reason, I’d like to write a quick note summarizing how to stay on the safe side and keep your scripts working across various systems.
Please note I will only cover the basic solution to the most important portability issues. If you’d like to know more about shebang handling in various systems, I’d like to recommend you an excellent article ‘The #! magic, details about the shebang/hash-bang mechanism on various Unix flavours’ by Sven Mascheck.
So, in order to stay portable you should note that:
- Many systems (Linux included!) have limits on shebang length. If you exceed this length, the kernel will cut the shebang in the middle of a path component, and usually try to execute the script with the partial path! To stay safe you need to keep shebang short. Since you can’t really control where the programs are installed (think of Prefix!), you should always rely on PATH lookups.
- Shebangs do not have built-in PATH lookups. Instead, you have to use the /usr/bin/env tool which performs the lookup on its argument (the exact path is mostly portable, with a few historical exceptions).
- Different systems split parameters in shebangs differently. In particular, Linux splits on the first space only, passing everything following it as a single parameter. To stay portable, you can not pass more than one parameter, and it can not contain whitespace. Which — considering the previous points made — means the parameter is reserved for program name passed to env, and you can not pass any actual parameters.
- Shebang nesting (i.e. referencing an interpreted script inside a shebang) is supported only by some systems, and only to some extent. For this reason, shebangs need to reference actual executable programs. However, using env effectively works around the issue since env is the immediate interpreter.
A few quick examples:
#!/usr/bin/env python # GOOD! #!python # BAD: won't work #!/usr/bin/env python -b # BAD: it may try to spawn program named 'python -b' #!/usr/bin/python # BAD: absolute path is non-portable, also see below #!/foo/bar/baz/usr/bin/python # BAD: prefix can easily exceed length limit #!/usr/lib/foo/foo.sh # BAD: calling interpreted scripts is non-portable