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.

DaveSource.com - Dave's geek site GetDave.com - all the current Dave Pointers. MarginalHacks - I have an elegant script for that, but it's too small to fit in the margin.