Взаимодействие с запущенным процессом

Давайте рассмотрим простой пример:

Process.run("ping", {"google.com"}) do |proc|
  proc.output.each_line { |line| puts line }
end

который запускает процесс, постоянно считывая его вывод и выводя вывод на стандартный вывод. В настоящее время, когда я нажимаю клавишу, она просто появляется вместе с выводом запущенного процесса, но я хотел бы иметь какую-то обработку клавиш, чтобы я мог управлять запущенным процессом с клавиатуры: остановить его, или перезапустите его с измененными аргументами. Как это сделать?


Или, чтобы сузить вопрос, как сделать эту пару выход-вход неблокирующей друг для друга? В настоящее время он делает один шаг, а затем ждет, пока не произойдет его аналог.

Process.run("ping", {"google.com"}) do |proc|
  until proc.output.closed?
    puts proc.output.gets
    puts "Got key: #{STDIN.raw &.read_char}"
  end
end

person WPeN2Ic850EU    schedule 10.07.2018    source источник


Ответы (2)


Использование терминала для интерактивного ввода не так просто, как может показаться. Вы можете попробовать с STDIN.raw &.read_char. Но это ограничено и, вероятно, далеко не уйдет.

Обычно для этого используется инструмент readline. В стандартной библиотеке есть привязки Crystal (см. Readline). В настоящее время они недокументированы, но должны работать. Вы также можете попробовать https://github.com/Papierkorb/fancyline, который является чистой реализацией Crystal readline.

person Johannes Müller    schedule 10.07.2018
comment
Спасибо за ответ, STDIN.raw &.read_char вполне подходит для меня, но другая проблема для меня заключается в том, как сделать эту пару ввода-вывода неблокирующей друг для друга? Я сделал дополнение к вопросу, чтобы проиллюстрировать проблему. - person WPeN2Ic850EU; 10.07.2018
comment
Вам необходимо запустить операцию копирования ввода-вывода и считыватель STDIN в отдельных волокнах. - person Johannes Müller; 10.07.2018
comment
Я пробовал это (без какого-либо успеха): ix.io/1gIT/ruby Это должно быть что-то очень простое Я не добираюсь сюда. - person WPeN2Ic850EU; 10.07.2018
comment
Вам нужна отдельная петля в обоих волокнах. - person Johannes Müller; 10.07.2018

В этом примере показано, как обрабатывать STDIN и один сеанс TCP. Это работает путем создания обработчиков волокон. С помощью дополнительных строк вы можете поделиться сеансом bash или REPL с несколькими клиенты.

пример 1: длительный процесс

#Long running Process, here an interactive shell for example
spawn do
    begin  
    cmd = "bash -i"
    Process.run(cmd, shell: true) do | p |
         pp p
         my.bashpid = p.pid
         my.bashinputfd = p.input.dup              #get copy of current fd
         p.input.puts("exec 2>&1")                 #stderr > stdout
         p.input.puts("echo connected to bash")    #send to STDIN of bash
          while line = p.output.read_line 
            puts line                     #send to STDOUT
            #send output of shell process to all online clients
            Mc.get_clients.each do |all|  
                     all.puts (line)   #send to Client
                   end  
          end
       end 

    rescue exception
      puts "Error: #{exception.message}"
      puts "Shell process has ended, program exits"
      exit
    end
end

пример 2:

require "socket"
#public vars
channel = Channel(String).new
csocket = Socket.tcp(Socket::Family::INET)
socket_conn=false
puts "Welcome:"

    spawn do
      server = TCPServer.new("0.0.0.0", 9090)
      loop do   #handle tcp client reconnects - do forever
       socket = server.accept
       csocket = socket.dup
       socket_conn=true
       p! socket_conn
       print "\r"

       while line = socket.gets
         channel.send(line)
       end
       socket_conn=false
       p! socket_conn
       print "\r"
      end
    end

    spawn do  #handle stdin input char by char - do until ctrl-c pressed
        while (char = STDIN.raw &.read_char) != '\u{3}'    #ctrl-c
        channel.send(char.to_s)
        end
        channel.send('\u{3}'.to_s)
    end

    loop do  #do until cttrl-c
      r = channel.receive
      if r == "\u0003"  #handle ctrl-c from channel
         break
      end
      p! socket_conn
      print "\r"
      p! r
      print "\r"
      if socket_conn 
           csocket.puts "got: #{r}"
      end 
      puts "got: #{r}"
      print "\r"
    end
person Peter Bauer    schedule 16.05.2020