【问题标题】:Run a command in current terminal in ruby then execute code when it exits在 ruby​​ 的当前终端中运行命令,然后在退出时执行代码
【发布时间】:2015-03-30 01:51:44
【问题描述】:

我需要的是:

  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​​ 代码

我已经阅读了这篇文章: 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​​ 进程捕获并杀死它而不是达到它的预期目标。

那么,有没有办法处理这些特殊要求?

【问题讨论】:

  • 您可以使用Ctrl + d 取消预处理中的输入。
  • 抱歉,问题不在于如何停止 cat 命令 ^^° 我只是使用 cat 命令来提供可以在任何 *nix 系统上运行的示例,但命令需要获取消息的是heroku工具带的heroku,并且ctrl-d不会在那里替换ctrl-c。
  • Ctrl + d 适用于任何从标准输入读取的 *nix 程序
  • 是的。虽然当程序不从标准输入读取时,我仍然非常希望有机会用一个大的 ctrl-c 来阻止它 ^^ 发送 ctrl-c 的能力真的是我的问题,而不是在标准输入上发送输入结束的能力。

标签: ruby shell unix


【解决方案1】:

非常感谢 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

【讨论】:

    【解决方案2】:

    我将为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"
    ...
    

    【讨论】:

    • 有趣的技巧,谢谢 ^^ 我刚刚尝试过,它似乎在给定命令(bash -c "echo test; sleep 3; echo test")下表现良好,但是当我尝试目标时失败了命令(heroku 运行控制台)。它不是预期的行为(与 irb 中的 ctrl-c 相同),而是突然停止 heroku 命令,并以“执行更多 ruby​​ 代码”语句退出(但不是“子进程已被杀死”语句)。请注意,使用“irb”命令,它的执行完全符合我的预期。
    • 我在“INT”捕获块中添加了 2 个报告行,以查看执行了 if 块的哪些分支;似乎只执行了主要的父进程分支( Process.kill("INT", $pid) ),所以我猜想向 heroku 命令发送“INT”与在其控制台中键入 ctrl-c 不同,无论出于何种原因。我正在尝试从这里调查...
    • (使用你的回答有点像黑板,对不起^^°)我尝试在控制台中打开一个到heroku的控制台,获取pid并从控制台发送一个带有kill的信号: 它表现得像它应该的那样。尝试使用您的示例,获取孩子的 pid 并定位子进程:它也按预期工作。有点令人费解。
    • 找到了一种方法来完成这项工作(见我的回答)!非常感谢您指出正确的方向^^d
    猜你喜欢
    • 1970-01-01
    • 2014-01-27
    • 2015-11-04
    • 1970-01-01
    • 2014-11-04
    • 2016-04-27
    • 1970-01-01
    • 2016-04-29
    • 1970-01-01
    相关资源
    最近更新 更多