运行 ruby 当前终端中的命令然后在退出时执行代码

Run a command in current terminal in ruby then execute code when it exits

我需要的是:

  1. 在调用系统命令之前执行某些操作。

  2. 执行我的系统命令

    1. 涉及提示并从用户那里获得答案

    2. 保持 ctrl-c 对被调用命令的影响不变

  3. 获取我的系统命令的结果并继续执行更多ruby代码

到目前为止,我尝试了类似的东西:

#!/usr/bin/env ruby
p "Foo"
exit_value = exec 'heroku run console'
p "Bar"
exit exit_value

这个失败是因为 exec 替换并终止了当前进程,所以在 exec

之后不再执行 ruby 代码

我已经读过这个post: How to run code after ruby Kernel.exec

我尝试使用 Kernel#system 调用:

#!/usr/bin/env ruby
p "Foo"
system 'heroku run console'
p "Bar"
exit $?

这个也失败了,因为 ctrl-c 显然被我的 ruby 进程捕获并杀死了它而不是到达预期目标。

那么,有没有办法处理这些奇特的需求呢?

我会为 SIGINT 安装一个信号陷阱,分叉子进程,执行命令(以防止父子进程中的信号处理程序 运行),如果 SIGINT 发生:

include Signal
include Process

# Define a signal handler for SIGINT
Signal.trap("INT") do
    if $pid != nil then
        # Kill the subprocess
        Process.kill("INT", $pid)
    else
        # Terminate ourself
        exit 1 
    end
end

# Fork off subprocess
# The $ marks the variable as global
$pid = fork
if $pid == nil then
    # Child code. Use exec(!) to avoid the signal handler 
    # getting called in the child.
    exec 'bash -c "echo test; sleep 3; echo test"'
end

# Wait for subprocess to exit
wait $pid
# Reset $pid
$pid = nil
exit_status = $?.exitstatus

# Detect whether the child process has been killed
if exit_status == nil then
    p "Child process has been killed."
end

p "Execute more ruby code"
...

非常感谢 hek2mgl 指出了正确的方向:

include Signal
include Process

# Handling SIGINT by doing nothing prevents default behaviour
# (killing both processes)
Signal.trap("INT") {}

# Fork off subprocess (so exec won't exit from your main process)
pid = fork
if pid == nil then
  # Child code. Use exec(!) to avoid the signal handler
  # getting called in the child.
  exec 'heroku run console'
else
  # Wait for subprocess to exit
  wait pid
  # "wait" sets the $? according to the subprocess exit status
  exit_status = $?.exitstatus

  p "Execute more ruby code"
  exit exit_status
end