Exececuting another application from within ruby

  • Thread starter Nigel Wilkinson
  • Start date
N

Nigel Wilkinson

Hi folks

I have a smell application from which I wish to display a file using an
external helper programme.

Lets say
app = the external programme
file = the file to be displayed

I've tried

system(app, file)

This works but it blocks my application. How can I fire up the helper
application in the background?

Cheers
Nigel
 
A

Ara.T.Howard

Hi folks

I have a smell application from which I wish to display a file using an
external helper programme.

Lets say
app = the external programme
file = the file to be displayed

I've tried

system(app, file)

This works but it blocks my application. How can I fire up the helper
application in the background?

def background command
Thread::new(command, Thread::current) do |cmd, cur|
begin
pipe = IO::popen cmd
true while pipe.gets and pipe.close
$?.exitstatus
rescue Exception => e
cur.raise e
end
end
end

thread = background 'long-running'

...
...
...

p thread.value # exitstatus

hth. if you need control over stdout and stderr check out session. it
provides thread safe execution.

require 'session'

def background command
sh = Session::new
Thread::new(command, Thread::current) do |cmd, cur|
begin
stdout, stderr = sh.execute cmd
[stdout, stderr, sh.status]
rescue Exception => e
cur.raise e
end
end
end

thread = background 'long-running'

...
...
...

p thread.value # [stdout, stderr, exitstatus]

session also allows thread safe stuff like

def background command, widget
sh = Session::new
Thread::new(command, Thread::current) do |cmd, cur|
begin
sh.execute(cmd) do |stdout, stderr|
widget.update stdout, stderr
end
sh.status
rescue Exception => e
cur.raise e
end
end
end


hth.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================
 
J

Joel VanderWerf

Nigel said:
Hi folks

I have a smell application from which I wish to display a file using an
external helper programme.

Lets say
app = the external programme
file = the file to be displayed

I've tried

system(app, file)

This works but it blocks my application. How can I fire up the helper
application in the background?

Thread.new {system(app, file)}

if all you want to do is start the process and you don't care about the
output or return value. Otherwise, see Ara's solutions.
 
A

Ara.T.Howard

Thread.new {system(app, file)}

if all you want to do is start the process and you don't care about the
output or return value. Otherwise, see Ara's solutions.

acutally, even if you do joel's may work

Thread.new{system(app, file); $?.exitstatus}

and then it's available via thread.value. this is much simpler than my
example. i've just gotten in the habit of using popen or session since i
invariably end up needing to provide stdin or get stderr, etc.

cheers.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================
 
B

Brian Candler

Thread.new {system(app, file)}

if all you want to do is start the process and you don't care about the
output or return value. Otherwise, see Ara's solutions.

It's worth mentioning that all that the system() call does is:

1. fork
2. exec (in child)
3. wait for child to terminate (in parent)

So a lower-level way to get what you want, without using Ruby threads, is:

pid = Process.fork do
exec(app, file)
end

and later on, you may wish to do

Process.waitpid(pid)

to reap the child and get its exit status. If it dies before then, it will
remain in the process table as a 'zombie' until you reap it or the parent
terminates. The Ruby thread solution effectively cleans up the child
automatically at the end for you.

If you want app to run as a daemon and never worry about when it terminates
(even if the Ruby program terminates first), then you can fork twice, run
the application in the grandchild, and the child terminates immediately:

pid = Process.fork do
Process.fork do
# normally you would redirect STDIN/STDOUT/STDERR here
exec(app, file)
end
exit
end
Process.waitpid(pid)
... continue here

I like being in full control like this, because system() hides the fact that
it is doing a fork, and fork can result in wierdness. For example, any
'atexit' script you've previously defined will run in both parent and child.
'teardown' scripts in Test::Unit can end up being called in both parent and
child. And so on.

Regards,

Brian.
 
N

nobuyoshi nakada

Hi,

At Tue, 5 Jul 2005 18:01:28 +0900,
Brian Candler wrote in [ruby-talk:147202]:
If you want app to run as a daemon and never worry about when it terminates
(even if the Ruby program terminates first), then you can fork twice, run

If the parent program terminates before the child, you don't
have to worry if the child becomes a zombie. Instead, It will
be adopted by the init process.
'atexit' script you've previously defined will run in both parent and child.

No, system nor exec don't invoke at_exit handlers.

$ ruby -e 'at_exit{puts "#$$ exiting"}; system("echo foo")'
foo
1020 exiting

$ ruby -e 'at_exit{puts "#$$ exiting"}; Process.waitpid(fork {exec("echo foo")})'
foo
1772 exiting

$ ruby -e 'at_exit{puts "#$$ exiting"}; Process.waitpid(fork {puts "foo"})'
foo
2228 exiting
2108 exiting
 
A

Ara.T.Howard

It's worth mentioning that all that the system() call does is:

1. fork
2. exec (in child)
3. wait for child to terminate (in parent)

So a lower-level way to get what you want, without using Ruby threads, is:

pid = Process.fork do
exec(app, file)
end

and later on, you may wish to do

Process.waitpid(pid)

to reap the child and get its exit status. If it dies before then, it will
remain in the process table as a 'zombie' until you reap it or the parent
terminates. The Ruby thread solution effectively cleans up the child
automatically at the end for you.

If you want app to run as a daemon and never worry about when it terminates
(even if the Ruby program terminates first), then you can fork twice, run
the application in the grandchild, and the child terminates immediately:

pid = Process.fork do
Process.fork do
# normally you would redirect STDIN/STDOUT/STDERR here
exec(app, file)
end
exit

# almost positively want 'exit!' here no?
# otherwise you'll call any at_exit handlers defined in the parent like
# removing tmpfiles....
end
Process.waitpid(pid)
... continue here

cheers.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| My religion is very simple. My religion is kindness.
| --Tenzin Gyatso
===============================================================================
 
J

Joel VanderWerf

Brian said:
It's worth mentioning that all that the system() call does is:

1. fork
2. exec (in child)
3. wait for child to terminate (in parent)

Not on windows. There's no fork(), so ruby uses CreateProcess to
implement #system.
 
N

Nigel Wilkinson

--On Wednesday, July 06, 2005 02:03:07 +0900 Joel VanderWerf
Not on windows. There's no fork(), so ruby uses CreateProcess to
implement #system.

Thanks everyone, I'll have a play when I get a bit of time.

Cheers
Nigel
 
B

Brian Candler

No, system nor exec don't invoke at_exit handlers.

$ ruby -e 'at_exit{puts "#$$ exiting"}; system("echo foo")'
foo
1020 exiting

It's clear that if the exec succeeds then no at_exit handler can possibly be
called; more interesting is the case if it fails. It looks like the at_exit
handler is not called there - I hadn't tested it before.

$ ruby -e 'at_exit{puts "#$$ exiting"}; system("zxcvzxcv"); p $?'
#<Process::Status: pid=31775,exited(127)>
31774 exiting
$

However:

$ ruby -e 'at_exit{puts "#$$ exiting"}; Process.waitpid(fork {exec("zxcvzxcv")}); p $?'
44096 exiting
-e:1:in `exec': No such file or directory - zxcvzxcv (Errno::ENOENT)
from -e:1
from -e:1:in `fork'
from -e:1
#<Process::Status: pid=44096,exited(1)>
44095 exiting

which is what I was expecting. Anyway, it's a useful mental note to keep :)

Cheers,

Brian.
 

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,175
Messages
2,570,942
Members
47,491
Latest member
mohitk

Latest Threads

Top