从Ruby运行并终止外部进程

时间:2016-06-09 05:06:47

标签: ruby process

我想在Ruby(MRI)流程的控制下运行外部流程。

有没有办法在Ruby下启动长/无限运行的作业,然后以编程方式控制其终止?我认为这会相对容易,但似乎真的很难找到一种方法来做到这一点。

一些示例代码,不起作用

pid = fork { system "yes > /dev/null" }
sleep 1  # yes is happily running
Process.kill "TERM", pid

如果我将其作为Ruby文件运行,它会很愉快地运行。但是,如果我pgrep yes,我发现yes仍在运行。如果我pkill yes它成功消失。 KILL信号不会比TERM信号更成功。

如何更改此代码以在后台运行yes,然后允许其终止?

(如果重要,我怀疑,这是在macOS上使用MRI Ruby 2.3(哈哈)10.11。)

1 个答案:

答案 0 :(得分:1)

你在这里得到了正确的想法但是你错过了一个微妙的事情。调用fork会创建一个子进程,但system也会执行此操作,因为它实现为fork / exec对,所以实际上你是杀死了错误的过程。

你可以告诉你puts pid是否在那里看看你想要杀死的过程。你试图杀死的那个通常是实际运行的yes进程的PID减一。

你可能想要什么:

pid = fork do
  exec "/usr/bin/yes", out: File::NULL
end

sleep 1

Process.kill 'TERM', pid

在使用exec时你必须​​非常小心,因为只要有一些不规则的东西,Ruby就会创建一个shell进程来执行你的命令,引入另一个子进程。杀死实际上不会杀死yes命令。在这里,我使用了out:选项来取消STDOUT,而不是依赖于自动使用shell包装器的> /dev/null技巧。

在一些快速测试中,我发现调用exec 'yes'会导致启动一个中间shell,但指定完整路径可以避免这种情况。