2016-12-16:
Shebang That Calls Ruby Rails Runner Script With Arguments
Problem
I wanted to create a ruby rails runner script that could just be
called as a shebang script, with the output sent to 'rails runner'
This would be something like:
#!/usr/bin/env rails runner
As is suggested by 'rails runner -h'.
Unfortunately most OS's do not allow anything more than
one argument on the shebang line. So effectively this ends up being:
/usr/bin/env "rails runner"
Which cannot, of course, find the command named 'rails runner'
This could be solved by making a wrapper script that calls rails
runner on the actual ruby script, but that bothers me, as I'd
prefer a single-file solution.
Rails Solution
The easy solution is to startup a ruby script that determines
if it is being run under Rails and, if not, starts itself up
as such. For example, put this at the top of your ruby script
#!/usr/bin/env ruby
exec("/usr/bin/env","rails","runner",File.expand_path($0),*ARGV) unless defined?(Rails)
Ruby Polyglot Solution
This doesn't work if you are trying to run a ruby script without
rails. You could use a secret argument to tell if you are being
called again, or, you could use the following ridiculous,
though working solution, to create a polyglot
(source that works under multiple languages). Fortunately only
the top of the file needs to be a polyglot to handle relaunching
as a ruby or rails script.
Here is the top of the file:
(in my case I use rbenv, so the full shebang is 'rbenv exec rails runner <script>')
##################################################
# polyglot to use shell to relaunch as 'rails runner'
##################################################
if [ "a" != "a" ]; then
printf "" # polyglot 'nop' - we are in ruby
else
# If only we could have this in the #!shebang, but most #! won't allow args
set "rbenv" "exec" "rails" "runner" "$0"
exec $@
fi
end
##################################################
# Ruby code below
##################################################
puts "Ruby code here"
I would love a better solution for the polyglot 'nop' than an empty
printf, but it basically works unless you are doing funky things with
I/O and can't have any output on stdout. The reason for the 'set' before
the 'exec' in the shell code is because it allows for the creation
of an array without using ',' - and ruby doesn't see any syntax errors
as it does with just "exec a b c..."
Gawk System Solution
And finally you can use a trick where you call 'system' in a single
gawk string to execute the command you want, which lets you effectively
pull this off in a single shebang line (presuming gawk is available):
#!/usr/bin/awk {system("rails runner " FILENAME); exit}
Back to Solutions.