Multiline shebang: Difference between revisions
No edit summary |
No edit summary |
||
Line 4: | Line 4: | ||
Occasionally, a more complex shebang line is needed. For example, some languages do not include the program name in ARGV; a multiline shebang can reorder the arguments so that the program name is included in ARGV. |
Occasionally, a more complex shebang line is needed. For example, some languages do not include the program name in ARGV; a multiline shebang can reorder the arguments so that the program name is included in ARGV. |
||
The syntax for a multiline shebang is complicated. The shebang lines must be simultaneously commented away from the main language and revealed to some shell (perhaps [[Bash]]) so that they can be executed. In other words, [[ |
The syntax for a multiline shebang is complicated. The shebang lines must be simultaneously commented away from the main language and revealed to some shell (perhaps [[Bash]]) so that they can be executed. In other words, [[http://en.wikipedia.org/wiki/Polyglot_(computing)|Polyglots]]. |
||
Warning: Using a multiline shebang of the form <code>#!/bin/sh ... exec ... !#</code> will set the code's mimetype to <code>text/x-shellscript</code>, which creates problems such as Emacs treating the file as a shell script, no matter which language and file extension it really uses. |
Warning: Using a multiline shebang of the form <code>#!/bin/sh ... exec ... !#</code> will set the code's mimetype to <code>text/x-shellscript</code>, which creates problems such as Emacs treating the file as a shell script, no matter which language and file extension it really uses. |
Revision as of 01:09, 19 April 2013
Simple shebangs can help with scripting, e.g. #!/usr/bin/env python at the top of a Python script will allow it to be run in a terminal as "./script.py".
Occasionally, a more complex shebang line is needed. For example, some languages do not include the program name in ARGV; a multiline shebang can reorder the arguments so that the program name is included in ARGV.
The syntax for a multiline shebang is complicated. The shebang lines must be simultaneously commented away from the main language and revealed to some shell (perhaps Bash) so that they can be executed. In other words, [[1]].
Warning: Using a multiline shebang of the form #!/bin/sh ... exec ... !#
will set the code's mimetype to text/x-shellscript
, which creates problems such as Emacs treating the file as a shell script, no matter which language and file extension it really uses.
C
#!/bin/bash sed -n -e '7,$p' < "$0" | /usr/bin/gcc -x c -o "$0.$$.out" - $0.$$.out "$0" "$@" STATUS=$? rm $0.$$.out exit $STATUS #include <stdio.h> int main(int argc, char **argv) { int i; for (i = 0; i < argc; i++) printf("argv[%d] -> %s\n", i, argv[i]); return 0; }
Test runs:
$ ./cmulshbang.c argv[0] -> ./cmulshbang.c.4062.out argv[1] -> ./cmulshbang.c $ ./cmulshbang.c 1 argv[0] -> ./cmulshbang.c.4071.out argv[1] -> ./cmulshbang.c argv[2] -> 1 $ ./cmulshbang.c 1 2 argv[0] -> ./cmulshbang.c.4080.out argv[1] -> ./cmulshbang.c argv[2] -> 1 argv[3] -> 2
Student exercise: use a stable filename for the executable, e.g. "$0.out"
. Do not remove it, and only recompile it if the script's timestamp is newer than that of the executable.
Clojure
The namespace = basename = filename minus the extension must be passed as a value to Clojure's -m flag.
<lang clojure>":";exec clj -m `basename $0 .clj` $0 ${1+"$@"} ":";exit</lang>
Common Lisp
Here, the script name is passed once to CLISP and once to ext:*args*, which normally omits it.
<lang lisp>#!/bin/bash
- |
exec clisp -q -q $0 $0 ${1+"$@"} exit |#</lang>
E
E uses only “#” for line comments, like the shell, so there is no straightforward answer. We can abuse the fact that “>” is also a line comment to achieve this effect. Note that a “>” line comment should ordinarily only occur as part of Updoc (test/documentation) text, so this is not good practice.
In this example, we are including the command name itself in the argument list, which would ordinarily not include it.
<lang e>#!/bin/sh >/dev/null; exec rune $0 $0 ${1+"$@"}
println(`I was called as ${interp.getArgs()[0]}.`)</lang>
Emacs Lisp
<lang lisp>:;exec emacs -batch -l $0 -f main $*</lang>
Erlang
hello.erl
<lang Erlang>#!/usr/bin/env escript
-module(hello). -export([main/1]).
main(_) -> io:format("Hello World!~n", []).</lang>
This works fine when the module is run by itself with dot slash:
<lang sh>$ ./hello.erl Hello World!</lang>
But when another Erlang module tries to import the code, or you try to compile manually in erl, you get a syntax error.
<lang sh>$ erl Erlang R14B03 (erts-5.8.4) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.8.4 (abort with ^G) 1> c(hello). ./hello.erl:1: syntax error before: '#' ./hello.erl:4: no module definition error</lang>
F#
<lang f#>#light (* exec fsharpi --exec $0 --quiet
- )
let main = printfn "Hello World"</lang>
Factor
Factor no longer requires a space after #!
as of v0.95.
<lang factor>#!/usr/bin/env factor -script</lang>
Forth
We can use Gforth's (non-ANS standard) support for shebangs and the '#' number prefix to make Gforth skip over the shebang without interfering with shell script interpretation.
<lang forth>#! /bin/sh
- 0 [IF] \ lines below read by shell but ignored by Gforth
exec gforth \ -m 256M \ -d 16M \ "$0" "$@"
[THEN] .( hello world) CR BYE </lang>
OCaml
ocamlc hates shebangs, so much trickery is needed. The number of underscores in the dummy kkkk identifier corresponds to the number of bash strings in the shebang. Thus, core library .cma files can be specified this way in interpreted mode, though accessing other OCaml scripts requires compiling them first, and referencing the .cmo's here.
<lang ocaml>if true then ignore begin let kkkk _ _ _ _ = 0 in kkkk "exec" "ocaml" "$0" "$@" + let fi = 0 and exit _ _ = 0 in if false then exit fi true else 0 end;;
let main = print_endline "Hello World!"</lang>
Example:
$ head -n 2 she.ml if true then ignore begin let kkkk _ _ _ _ _ _ = 0 in kkkk "exec" "ocaml" "$0" "unix.cma" "graphics.cma" "$@" + let fi = 0 and exit _ _ = 0 in if false $ ocaml she.ml Hello World! $ /bin/bash she.ml Hello World! $ ocamlc -o she.byte she.ml $ ./she.byte Hello World! $ ocamlopt -o she.opt she.ml $ ./she.opt Hello World!
PARI/GP
The PARI equivalent to a multiline shebang is a collection of GP;
lines:
<lang C>/*
GP;install("C_function_name","G","GP_name","./filename.gp.so");
GP;addhelp(GP_name, "GP_name(n): Computes the foo of bar(n).");
- /</lang>
These commands are passed to GP when invoked by gp2c.
Perl
From perldoc perlrun
, the following is supposed to find perl one way or another under sh, csh or perl.
<lang perl>#!/usr/bin/perl
eval '(exit $?0)' && eval 'exec perl -wS $0 ${1+"$@"}'
& eval 'exec /usr/bin/perl -wS $0 $argv:q'
if $running_under_some_shell;</lang>
Perl 6
<lang perl6>#!/usr/local/bin/perl6 eval '(exit $?0)' && eval 'exec perl6 $0 ${1+"$@"}' & eval 'exec perl6 $0 $argv:q'
if 0;</lang>
PicoLisp
We can use a multi-line comment #{ ... }# to hide the shell commands from Lisp. The opening #{ in turn is a coment for the shell. <lang PicoLisp>#!/bin/bash
- {
exec pil $0 foo bar
- }#
- Lisp code
(println (cadr (file)) (opt) (opt)) (bye)</lang> Output:
$ ./myScript "myScript" "foo" "bar"
Pike
we use a multiline comment to hide the shell command from pike, and we can use a preprocessor directive to hide the comment begin from the shell. <lang Pike>#!/bin/bash
- define foo foo /*
exec pike $0 hello world
- /
int main(int argc, array argv) {
write("%O\n", argv);
}</lang>
output:
({ /* 3 elements */ "/local/users/mbaehr/src/pike/multiline-shebang/multiline-shebang.pike", "hello", "world" })
Python
We can use multiple strings to make the shell commands do nothing from Python (actually they become the module docstring.). <lang Python>#!/bin/bash "exec" "python" "$0"
print "Hello World"</lang> Output:
$ ./myScript Hello World
Control structures (if/for/etc.) can't be quoted, but one can use the following to embed any script: <lang Python>#!/bin/sh "true" \' if [ -L $0 ]; then ... exec "$interpreter" "$@" exit 127
__doc__ = """module docstring"""
print "Hello World"</lang>
Here we use a) the code '''\' translates to \ in shell, but opens a multi-line string in Python; b) the true command ignores its argument, c) we always exit before the ending ''' so that the shell interpreter never reads it. Also, remember to set any docstrings by assigning to __doc__ since the docstring is already used for the shell script.
Ruby
One can use a single-line shebang, like #!/usr/bin/env ruby
, and use Kernel#system or `backquotes` to run any extra shell commands. A multi-line shebang is possible, but not necessary.
This script works both ways: either /bin/sh script.rb
or ruby script.rb
would run multiple lines of shell commands, and then start Ruby.
<lang ruby>#!/bin/sh
- Insert shell code here!
printf '%s\n' "Shell running $0" i=1 for arg do
printf ' %s\n' "\${$i}: $arg" i=`expr $i + 1`
done
- Switch from shell to Ruby.
exec ${RUBY-ruby} -x "$0" --coming-from-sh "$@"
- !ruby
ARGV[0] == "--coming-from-sh" or exec "/bin/sh", $0, *ARGV ARGV.shift
- Insert Ruby code here!
puts "Ruby running #$0" ARGV.each_with_index do |arg, i|
puts " ARGV[#{i}]: #{arg}"
end</lang>
When running /bin/sh scratch.rb
, the shell:
- ignores
#!/bin/sh
, because it is a comment. - runs multiple lines of shell code.
- executes
ruby -x
; user can set RUBY environment variable to pick different Ruby, like RUBY=ruby19 or RUBY=jruby.
ruby -x
skips every line until the first Ruby shebang. This line must start with "#!" and must contain "ruby". (So "#!ruby" is the shortest shebang to work.)
When running ruby scratch.rb
(without -x option), Ruby notices that the first line "#!/bin/sh" is a foreign shebang.
- Ruby 1.8 then interprets this shebang and executes /bin/sh.
- Ruby 1.9 then assumes -x option and skips to the first Ruby shebang. The script is not
--coming-from-sh
, so it executes /bin/sh.
Scala
The Scala interpreter (scala) supports shebangs, but the compiler (scalac) does not.
<lang scala>#!/usr/bin/env scala</lang>
Scheme
#| ... |#
provides just the right environment for the multiline shebang. Here, the script name is passed once to the Chicken Scheme Interpreter and once to be picked up in args.
<lang scheme>#!/bin/bash
- |
exec csi -ss $0 ${1+"$@"} exit |#</lang>
Smalltalk
<lang smalltalk>"exec" "gst" "-f" "$0" "$0" "$@" "exit"</lang>
Tcl
It is normal to use a line like this: <lang tcl>#!/usr/bin/env tclsh</lang> But in cases where that is not enough perhaps because it needs some logic to locate the Tcl interpreter to use the differences in the way Tcl and the Bourne shell interpret end-of-line backslashes in comments can be used: <lang tcl>#!/bin/sh
- Next line is comment in Tcl, but not in sh... \
exec tclsh "$0" ${1+"$@"}</lang> Additional complexity can be added so long as the lines for the shell are commented in a Tcl sense.
TXR
#!/bin/sh sed -n -e '4,$p' < "$0" | /usr/bin/txr - "$0" "$@" exit $? @(next :args) @(collect) @arg @(end)
Test run:
$ ./multilineshebang.txr arg[0]="./multilineshebang.txr" $ ./multilineshebang.txr 1 arg[0]="./multilineshebang.txr" arg[1]="1" $ ./multilineshebang.txr 1 2 3 arg[0]="./multilineshebang.txr" arg[1]="1" arg[2]="2" arg[3]="3" $