In ruby, is it possible to specify to call another ruby script using the same ruby interpreter as the original script is being run by?
For example, if a.rb runs b.rb a couple of times, is it possible to replace
system("ruby", "b.rb", "foo", "bar")
with something like
run_ruby("b.rb", "foo", "bar")
so that if you used ruby1.9.1 a.rb
on the original, ruby1.9.1
would be used on b.rb, but if you just used ruby a.rb
on the original, ruby
would be used on b.rb?
I'd prefer not to use shebangs, as I'd like it to be able to run on different computers, some of which don't have /usr/bin/env
.
Edit: I didn't mean load
or require
and the like, but spawning new processes (so I can use multiple CPUs).
7 Answers 7
require "b.rb"
will execute the contents of b.rb (you call leave off the ".rb", and there is a search path). In your case, you would probably do something like:
a.rb:
require "b.rb";
b("Hello", "world")
b.rb:
def b(first, second)
puts first + ", " + second
end
Note that if you use require, Ruby will only load and execute the file once (every time you call load it will be reloaded), but you can call methods defined in the file as many times as you want.
As things get more complex, you will want to move towards an object-oriented design.
EDIT: In that case, you should look into Ruby threading. A simple example is:
a.rb:
require "b";
t1 = Thread.new{b("Hello", "world");}
t2 = Thread.new{b("Hello", "galaxy");}
t1.join
t2.join
b.rb:
def b(first, second)
10.times {
puts first + ", " + second;
sleep(0.1);
}
end
-
That wasn't what I was meaning. Do you understand what I'm after with the edit I've made?Andrew Grimm– Andrew Grimm2010年04月14日 05:57:07 +00:00Commented Apr 14, 2010 at 5:57
-
Apart from JRuby, that doesn't allow you to truly use multiple processors.Andrew Grimm– Andrew Grimm2010年04月14日 06:16:44 +00:00Commented Apr 14, 2010 at 6:16
Avdi Grimm wrote a series of articles on the Devver blog about different ways to start Ruby subprocesses last summer:
- A Dozen (or so) Ways to Start Subprocesses in Ruby: Part 1
- A Dozen (or so) Ways to Start Subprocesses in Ruby: Part 2
- A Dozen (or so) Ways to Start Subprocesses in Ruby: Part 3
- Beware of pipe duplication in subprocesses
[Note: it appears that part 4 hasn't been published yet.]
-
If something as thorough as Avdi's articles has to resort to using
File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
, it's a good sign there isn't any better alternative!Andrew Grimm– Andrew Grimm2010年04月27日 07:17:32 +00:00Commented Apr 27, 2010 at 7:17 -
7this is not an answeruser4278778– user42787782016年03月22日 12:23:57 +00:00Commented Mar 22, 2016 at 12:23
If you just want to run a script in the context of an existing process, you can also do this
eval File.read("/path/to/your/script.rb")
Not sure what your use case is but this could be useful for example if you have a Rails console open and you want to execute some code in a scratch file, but don't want to have to keep copying the entire block of code into your console.
The require trick is a good idea, assuming the script in question doesn't choke trying to redefine any constants you may have set, or calling methods on objects you may have runtime monkey patched to no longer honor their standard contracts.
In either case, the problem is less the approach than it is the code in the scripts themselves. Show good manners, put your constants in a namespace, and don't monkey patch the runtime desctructively.
To ensure the script in question doesn't mess with the runtime of your calling script, and to guard against the chance it might call Kernel/Process.exit() somewhere, try the following
pid=Process.fork do
require 'script.rb'
Process.exit
end
ignored, status = Process.waitpid2(pid, Process::WNOHANG)
puts "script.rb PID #{pid} exited, exit status: #{status.exitstatus}"
For more advanced things like writing to its stdin stream or reading from its stdout or stderr streams, use the Open4 gem.
This is what I have used
/myapp/script/main_script.rb
load rails env, only if you need to
ENV['RAILS_ENV'] = ARGV.first || ENV['RAILS_ENV'] || 'development'
require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
run other slave scripts from within main_script.rb
require File.expand_path(File.dirname(__FILE__) + "/../script/populate_wh_grape_varieties_table.rb")
-
This won't help me, but it may help people who use Rails.Andrew Grimm– Andrew Grimm2012年08月02日 23:23:08 +00:00Commented Aug 2, 2012 at 23:23
Charles Nutter, of JRuby fame, is suggesting a Kernel#ruby
method to call a Ruby script using the same Ruby implementation as you're currently using.
Edit: the proposal was rejected. Matz said that MVM (multiple virtual machines) may provide the solution.