运行 并终止来自 Ruby 的外部进程

Running and terminating an external process from Ruby

我想 运行 一个受 Ruby (MRI) 进程控制的外部进程。

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

一些示例代码,不起作用:

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

如果我 运行 这是一个 Ruby 文件,它 运行 很高兴。但是,如果我然后 pgrep yes 我看到 yes 仍然是 运行ning。如果我 pkill yes 它成功消失了。 KILL 信号并不比 TERM 信号更成功。

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

(以防万一,我对此表示怀疑,这是在 macOS(哈哈)10.11 上使用 MRI Ruby 2.3。)

你的想法是正确的,但你错过了一个微妙的东西。调用 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,而不是依赖 > /dev/null 技巧,它会自动使用 shell 包装器。

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