Executing a system command and stopping it after a specified duration?

I'd like to run a system command and then stop it after specified amount of time (in seconds or milliseconds.) What's the best way to do it?

e.g.

exec("cat /dev/video > test.mpg")

then kill the process after so many minutes, seconds, etc...

Also, if there is a better Ruby way to do this other than "cat", I'm all ears...

Thanks.

I'd like to run a system command and then stop it after specified
amount of time (in seconds or milliseconds.) What's the best way to
do it?

In Perl it was done by setting the signal ALRM (or using the "alarm"
command). I'm not sure how this is done in Ruby as I haven't had a
use for signal trapping since using Ruby.

Also, if there is a better Ruby way to do this other than "cat", I'm
all ears...

# assuming binary file (i.e. mpg)
File.open("/dev/video", "rb") do |inf|
  File.open("test.mpg", "wb") do |outf|
    inf.each_byte {|byte| outf.putc byte}
  end
end

···

On Apr 14, 9:57 pm, Robert La Ferla <robertlafe...@comcast.net> wrote:

Robert La Ferla wrote:

I'd like to run a system command and then stop it after specified amount
of time (in seconds or milliseconds.) What's the best way to do it?

e.g.

exec("cat /dev/video > test.mpg")

then kill the process after so many minutes, seconds, etc...

Also, if there is a better Ruby way to do this other than "cat", I'm all
ears...

Thanks.

# from "ri Timeout":
require 'timeout'
status = Timeout::timeout(500) {
  exec("cat /dev/video > test.mpg")
}

Dan

Out of curiosity I probed deeper and it looks like you're looking for
Timeout#timeout (see RDoc/ri for more information). Here is an
example:

require 'timeout'

# raises an exception if timeout is met
begin
  Timeout::timeout(5) { sleep 10 }
rescue Timeout::Error
  puts "It timed out!"
end

···

On Apr 14, 9:57 pm, Robert La Ferla <robertlafe...@comcast.net> wrote:

I'd like to run a system command and then stop it after specified
amount of time (in seconds or milliseconds.) What's the best way to
do it?

Robert La Ferla wrote:

I'd like to run a system command and then stop it after specified amount
of time (in seconds or milliseconds.) What's the best way to do it?

e.g.

exec("cat /dev/video > test.mpg")

then kill the process after so many minutes, seconds, etc...

Once you call exec the ruby program does not run anymore.

From the description of Kernel#exec
http://www.ruby-doc.org/core/classes/Kernel.html#M005957

Replaces the current process by running the given external command. If
exec is given a single argument, that argument is taken as a line that
is subject to shell expansion before being executed.

you may want to use

system("cmd") module Kernel - RDoc Documentation
`cmd` module Kernel - RDoc Documentation
%x { cmd }

wrapped in timeout

Also, if there is a better Ruby way to do this other than "cat", I'm all
ears...

I'm using `mencoder ...` with options set via string substitution to
record TV-series

channel = ARGV[0]
outfilename = ARGV[1]
duration = ARGV[2]

`mencoder dvb://#{channel} -vf lavcdeint -o #{outfilename} -endpos
#{duration} -oac lavc -ovc lavc -lavcopts
acodec=mp2:vcodec=mpeg2video:vhq:vbitrate=2048`

If you want to read from '/dev/video' you could combine james.d.masters
and Dan Zwells suggestions - but using system or `

require "timeout"
begin
  Timeout::timeout(600) do
    system("cat /dev/video > test.mpg")
    # `cat /dev/video > test.mpg`
  end
rescue Timeout::Error
  puts("Done")
end

or

require "timeout"
begin
  Timeout::timeout(600) do
    File.open("/dev/video", "rb") do |input|
      File.open("test.mpg", "wb") do |output|
        input.each_byte {|byte| output.putc(byte)}
      end
    end
  end
rescue Timeout::Error
  puts("Done")
end

Stefan

I have this handy function ; it raises an exception on timeout/non
zero exit status, returns the result of the call
otherwise (ie. what the process output on $stdout). In case of timeout
it also attemts to kill the process (runs on Unix - no clue about
other platforms!) :

···

On Apr 15, 5:57 am, Robert La Ferla <robertlafe...@comcast.net> wrote:

I'd like to run a system command and then stop it after specified
amount of time (in seconds or milliseconds.) What's the best way to
do it?

#
# Usage example :
#

files = run('find -mtime -7')

#
# Function :
#
def run(s, maxt = 10*60)
  begin
    pipe = IO.popen(s, 'r')
  rescue Exception => e
    $stderr << 'Execution of '+s+' has failed :' + e.to_s
    raise "run failed"
  end

  begin
    Timeout::timeout(maxt) do |t|
      a = Process.waitpid2(pipe.pid)
      if a[1].exitstatus != 0
        $stderr << 'Error while executing ' + s
        raise "run failed"
      end
    end
  rescue Timeout::Error => e
    $stderr << 'Execution of '+s+' has timed out. Killing subprocess'
    begin
      Process.kill('KILL', pipe.pid)
      pipe.close
    rescue Object => e
      $stderr << 'Failed killing process : ' + e.to_s
    end
    raise "run failed"
  end

  out = pipe.gets(nil)
  pipe.close
  out
end

I've edited it a bit to remove some of my specific dependencies - so
there might be typos here :slight_smile:
It could be improved to also return the content of $stderr in case of
non-zero exit status.

Anselm

Thanks.

A couple of issues:

1. The time out is working but the "cat" process is not terminated. I tried both system(cmd) and `#{cmd}`.

2. I wonder if Ruby is fast enough to read /dev/video and dump it to a file without a delay. Imagine if this is running for a couple of hours...

BTW - My configuration is:

  % ruby --version
ruby 1.8.5 (2007-03-13 patchlevel 35) [i386-linux]

Fedora Linux 2.6.20-1.2933.fc6

···

On Apr 15, 2007, at 3:39 AM, Stefan Mahlitz wrote:

If you want to read from '/dev/video' you could combine james.d.masters
and Dan Zwells suggestions - but using system or `

require "timeout"
begin
  Timeout::timeout(600) do
    system("cat /dev/video > test.mpg")
    # `cat /dev/video > test.mpg`
  end
rescue Timeout::Error
  puts("Done")
end

or

require "timeout"
begin
  Timeout::timeout(600) do
    File.open("/dev/video", "rb") do |input|
      File.open("test.mpg", "wb") do |output|
        input.each_byte {|byte| output.putc(byte)}
      end
    end
  end
rescue Timeout::Error
  puts("Done")
end

Maybe you will need to call the "kill" command... I'm not sure if
child processes die with an exception being raised or not.

In any case, I'd use the alternatives mentioned here instead of using
"system". They are cleaner IMHO.

···

On Apr 15, 5:30 pm, Robert La Ferla <robertlafe...@comcast.net> wrote:

1. The time out is working but the "cat" process is not terminated.
I tried both system(cmd) and `#{cmd}`.

require 'rubygems'
require 'open4' ### gem install open4

Open4.spawn 'cat /dev/video', :timeout=>600, :stdout=>buf=''

p buf.size

-a

···

On Mon, 16 Apr 2007, Robert La Ferla wrote:

On Apr 15, 2007, at 3:39 AM, Stefan Mahlitz wrote:

If you want to read from '/dev/video' you could combine james.d.masters
and Dan Zwells suggestions - but using system or `

require "timeout"
begin
Timeout::timeout(600) do
   system("cat /dev/video > test.mpg")
   # `cat /dev/video > test.mpg`
end
rescue Timeout::Error
puts("Done")
end

or

require "timeout"
begin
Timeout::timeout(600) do
   File.open("/dev/video", "rb") do |input|
     File.open("test.mpg", "wb") do |output|
       input.each_byte {|byte| output.putc(byte)}
     end
   end
end
rescue Timeout::Error
puts("Done")
end

Thanks.

A couple of issues:

1. The time out is working but the "cat" process is not terminated. I tried both system(cmd) and `#{cmd}`.

2. I wonder if Ruby is fast enough to read /dev/video and dump it to a file without a delay. Imagine if this is running for a couple of hours...

BTW - My configuration is:

% ruby --version
ruby 1.8.5 (2007-03-13 patchlevel 35) [i386-linux]

Fedora Linux 2.6.20-1.2933.fc6

--
be kind whenever possible... it is always possible.
- the dalai lama