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

R

Robert La Ferla

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

james.d.masters

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
 
D

Dan Zwell

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
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
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFGId0kUYnvW+YuEvYRAkc0AKDBBveie/L0/2ePNqkLKYKvqz4fTACgso+d
g3U+yVpF+5pFf3RnGb+fS9o=
=B+zE
-----END PGP SIGNATURE-----
 
J

james.d.masters

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?

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
 
S

Stefan Mahlitz

Robert said:
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") http://www.ruby-doc.org/core/classes/Kernel.html#M005960
`cmd` http://www.ruby-doc.org/core/classes/Kernel.html#M005979
%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
 
R

Robert La Ferla

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
 
J

james.d.masters

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

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

anselm

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?

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!) :

#
# 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 :)
It could be improved to also return the content of $stderr in case of
non-zero exit status.

Anselm
 
A

ara.t.howard

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

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


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

p buf.size


-a
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,240
Messages
2,571,211
Members
47,845
Latest member
vojosay

Latest Threads

Top