无法在嵌套的Ruby进程中捕获INT/TERM信号

cyvaqqii  于 2023-05-28  发布在  Ruby
关注(0)|答案(1)|浏览(112)

我想写一个脚本,它能做两件事:(1)启动一个代理,(2)运行一个带有pry和自定义提示符的rails控制台
问题是我不知道如何防止Ctrl+C中断到达代理进程。
以下是我尝试过的:

  • 在执行的几个不同部分捕获INTTERM信号
  • 使用proxy命令生成一个bash脚本并运行
  • 运行一个单独的脚本来运行代理作业和控制台/窥探代码

到目前为止没有任何工作。
如果您有任何建议,我们将不胜感激。
这里有一个测试脚本来试用它。以下是你需要的:

  • pry已安装
  • rails应用程序根文件夹中的脚本
  • 在其上运行chmod +x,使其可执行
#!/usr/bin/env ruby

class TestInterrupt
  include Process
  include Signal

  def start
    override_interrupt
    start_script
    start_console

    exit
  end

  private

  attr_reader :bash_job, :console_job

  def override_interrupt
    trap("INT")  { puts "global INT. Run `!!!`, `quit`, or `exit` to terminate"  }
    trap("TERM") { puts "global TERM. Run `!!!`, `quit`, or `exit` to terminate" }
    puts "=== Trapping global INT, TERM ==="
  end

  def start_script
    @bash_job = fork do
      trap("INT")  { puts "bash_job INT" }
      trap("TERM") { puts "bash_job TERM" }
      puts "=== Trapping bash_job INT, TERM ==="
      exec("echo Starting shell script && sleep 5 && echo Ending shell script")
    end
    Process.detach(@bash_job)
  end

  def start_console
    @console_job = fork do
      trap("INT")  { puts "console_job INT" }
      trap("TERM") { puts "console_job TERM" }
      puts "=== Trapping console INT, TERM ==="
      exec console_code
    end
    puts "=== Starting rails console (PID: console_job) ==="
    wait(console_job)
  end

  def console_code
    <<~BASH
      rails runner '#{pry_code}'
    BASH
  end

  def pry_code
    <<~RUBY
      require "pry"

      pry_prompt = Pry.config.prompt
      env_prompt = Pry::Helpers::Text.cyan("TestInterrupt")
      Pry.config.prompt = [
        proc { |*a| "\#{env_prompt} \#{pry_prompt.first.call(*a)}"  },
        proc { |*a| "\#{env_prompt} \#{pry_prompt.second.call(*a)}" },
      ]

      Signal.trap("INT") { puts "inner INT" }
      Signal.trap("TERM") { puts "inner TERM" }
      puts "=== Trapping inner INT, TERM ==="

      pry
    RUBY
  end
end

TestInterrupt.new.start
cpjpxq1n

cpjpxq1n1#

我知道INT是CTRL-C,CTRL-D不是信号,所以我只能调试一下:

# its_a_trap.rb

begin
  trap("INT") { puts " INT" }
  sleep
ensure
  puts "exit"
end
$ ruby its_a_trap.rb
^C INT

工作,不退出。

begin
  trap("INT") { puts " INT" }
  system "trap 'echo shell received INT signal' INT; sleep infinity"
ensure
  puts "exit"
end
$ ruby its_a_trap.rb
^C INT
shell received INT signal
exit

不管用

begin
  trap("INT", "IGNORE")
  system "trap 'echo shell received INT signal' INT; sleep infinity"
ensure
  puts "exit"
end
$ ruby its_a_trap.rb
^C^C^C^C^C

有效但不退出。眼见为实watch -n 1 ps ft pts/4

2293186 pts/4    Ss     0:00 zsh
2350743 pts/4    S+     0:00  \_ ruby its_a_trap.rb
2350757 pts/4    S+     0:00      \_ sh -c trap 'echo shell received INT signal' INT; sleep infinity
2350758 pts/4    S+     0:00          \_ sleep infinity

forks和execs以及rails console也是一样:

fork do
  trap("INT") { puts "defunct" } # i don't know if it does anything here
  exec %(trap 'echo shell received INT signal' INT; sleep 20)
end

Process.detach(
  fork do
    trap("INT", "IGNORE") # this does something
    exec %(_='im sleeping'; sleep 20)
  end
)

trap("CLD") { puts "child process died" }
trap("INT") { puts "dont kill wait INT" }
Process.wait(
  # it was the same with pry, no difference
  fork { exec "rails console" }
)
$ ruby its_a_trap.rb
Loading development environment (Rails 7.0.5)
>> dont kill wait INT
^C
shell received INT signal
child process died
>>

其中一个没能挺过来

2293186 pts/4    Ss     0:01 zsh
2359797 pts/4    Sl+    0:00  \_ ruby its_a_trap.rb
2359811 pts/4    Z+     0:00      \_ [sh] <defunct>
2359812 pts/4    S+     0:00      \_ sh -c _='im sleeping'; sleep 20
2359816 pts/4    S+     0:00      |   \_ sleep 20
2359814 pts/4    Sl+    0:01      \_ /home/alex/.rbenv/versions/3.2.2/bin/ruby bin/rails console

相关问题