This issue looks easy, but is actually complex.
On shell, you may run a command asynchronously just by adding &
at the end of command.
$ sleep 10 &
[1] 8048
You can obtain the process ID, so you can stop the command.
$ kill 8048
How can we run an external command on Ruby?
system()
The easiest way to run an external command is system()
.
system 'sleep 10 &'
The return value of system()
is the exit code. In this case, the system()
returns true immediately after that ran, but it doesn't return process ID. To obtain the process ID, you can use $?
.
system 'sleep 10 &'
p $? #=> 8050
But unfortunatelly the $?
is not the processs ID of sleep
, but sh -c sleep 10
. There is a cussion. You cannot obtain the process ID of sleep
directly.
One more problem: the notation is available only in UNIX based platform.
system() without shell
system 'sleep 10'
uses shell but system 'sleep', '10'
doesn't use shell. Separate the arguments. But system 'sleep', '10', '&'
is not available.
Thread
To emulate '&'
feature, how about using Ruby's asynchronous features? Thread is the easiest way of that.
t = Thread.start do
system 'sleep', '10'
end
t.kill
Looks good, but it doesn't work. Thread certainly will stop, but the external command booted by the thread will still alive.
exec()
How about using another way of booting an external command? There is exec()
, which roughly means the combination of system()
and exit()
.
exec 'ls'
roughly means
system 'ls'
exit
So you think that the following code will work well.
t = Thread.start do
exec 'sleep', '10'
end
t.kill
Unfortunatelly it doesn't work. You cannot use exec()
in a child thread.
fork()
Look for another way of supporting asynchronous feature on Ruby. fork()
. fork()
copies the Ruby process itself.
pid = fork do
exec 'sleep', '10'
end
Process.kill pid
It works! fork()
is awesome!
But there is a bad news. Thread
works on all platforms, but fork
works on only some platforms. I'm sorry Windows users and NetBSD users.
spawn()
spawn = fork + exec. spawn()
works on all platforms! It's perfect! But... ruby 1.8 doesn't have spawn()
...
Summary
- Ruby 1.9 + UNIX: Use
fork
and exec
, or use spawn
.
- Ruby 1.8 + UNIX: Use
fork
and exec
.
- Ruby 1.9 + Windows: Use
spawn
.
- Ruby 1.8 + Windows: Use other OS, other Ruby, or consider using this library