How to Make a Ruby Script Run Once a Second

How do I make a Ruby script run once a second?

There are a few ways to do this.

The quick-and-dirty versions:

  1. shell (kornish):

    while :; do
    my_ruby_script.rb
    sleep 1
    done
  2. watch(1):

    shell$ watch -n 1 my_ruby_script.rb

    This will run your script every second and keep the output of the most recent run displayed in your terminal.

  3. in ruby:

    while true
    do_my_stuff
    sleep 1
    end

These all suffer from the same issue: if the actual script/function takes time to run, it makes the loop run less than every second.

Here is a ruby function that will make sure the function is called (almost) exactly every second, as long as the function doesn't take longer than a second:

def secondly_loop
last = Time.now
while true
yield
now = Time.now
_next = [last + 1,now].max
sleep (_next-now)
last = _next
end
end

Use it like this:

secondly_loop { my_function }

Running another ruby script from a ruby script

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.]

A ruby script to run other ruby scripts

You can use load to run the script you need (the difference between load and require is that require will not run the script again if it has already been loaded).

To make each run have different arguments (given that they are read from the ARGV variable), you need to override the ARGV variable:

(1..6).each do |i|
ARGV = ["cmd_line_arg_#{i}","cmd_line_arg2"]
load 'program1.rb'
end

Ruby running two scripts with mulithreading

So one possible solution I've found is

system("ruby app.rb & ruby app2.rb")

This only works however if running from linux I think however so I would still appreciate any further solutions.

Run two ruby scripts from rake task

It might be easier to require the ruby class directly into your rake task, but if you want to run a script from a rake task you can run any shell code in Ruby using backticks, like this:

`ruby first_script.rb param1 param2 | ruby second_script.rb`

ruby script - redirect output from one script to another script

Instead of reading just one line (msg = gets.chomp) you need to iterate through the lines of stdin

$stdin.each_line do |msg|
# ...
end

This does not wait for the entire output to be generated, it will process the stream as lines are printed by the first process (ignoring buffering).

For example, with these two scripts

# one.rb
i = 0
loop { puts i += 1 }

# two.rb
$stdin.each_line { |msg| puts "#{msg.chomp}!" }

The first has infinite output, but you will still see output when you run

ruby one.rb | ruby two.rb

ping function every x amount seconds

If you simply sleep for a constant amount of time as suggested in other answers, the error will contaminate as it keeps running, and will not be accurate. In fact, each iteration would take longer than the given interval.

The answer shown below adjusts the lag each time per iteration.

module Kernel
def tick_every sec, &pr
Thread.new do loop do
pr.call
t = Time.now.to_f
frac = t.modulo(sec.to_f)
sleep(sec - frac)
end end
end
end

thread = tick_every(2) do
puts "foo"
end
...
some_other_tasks
...
thread.kill

How to execute Ruby files from another Ruby file

In general your code isn't written in the Ruby way. This is untested but it looks about right:

class Launch

FILES = ['sum_of_digits', 'compressed_sequence', 'shortest_repetition']

def get_program
FILES.each_with_index do |fname, i|
puts "#{i} . #{fname}"
end

loop do
puts "Enter program number to execute: "
program_number = gets.to_i
file_to_load = FILES[program_number]
puts "loading program #{file_to_load}"

begin
system("ruby #{file_to_load}.rb #{file_to_load}.txt")
rescue => e
puts "loading error: #{e}"
puts "'#{file_to_load}' cannot be loaded, it may have been moved or not exist."
end

puts 'Do you want to continue Y/N'
break if gets.chomp.strip.upcase == 'N'
end
end
end

launch = Launch.new
launch.get_program

Some things to study:

  • block and end are used to start exception handling, not to define control loops. Well, they can, but there are better, more idiomatic, ways. loop is recommended by Matz.
  • You used load but I don't think that's really what you'd want to do. Instead, you should tell the OS to load and run the code in a sub-shell using system, not in the context of your currently running code.
  • Instead of using a bare rescue, your code should at least capture the exception using rescue => e so you can output what occurred. In "real life", AKA, production, you should be even more discerning and capture only the exceptions you expect, but that's a different discussion.
  • When using a begin/rescue/end, try to keep them as small as possible, at least until you're more familiar with how they work. rescue is a great way to shoot yourself in the foot, and debugging raised exceptions that could be generated by many lines of code can be a pain.

In general, when you have a list of things that's likely to change, or any variable that's more likely to change than the rest of the code, put that definition at the top of the script, or the top of the class or module definition, then reference it as a constant. That helps avoid magical dust being sprinkled through the code that has to be searched for if you want to add or delete things. Like files. Or magical dust.

Issue One command and Run Multiple Ruby Files

I would do it ruby-style and use rake gem.

I would create file named "rakefile.rb" and this would be its content:

task :default do
FileList['file*.rb'].each { |file| ruby file }
end

Then I would call rake in my favourite shell and I would enjoy it.

Bonus: It's multiplatform.



Related Topics



Leave a reply



Submit