加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ruby – Runy Open3.popen3从命令行输入子进程的输入

发布时间:2020-12-17 02:23:55 所属栏目:百科 来源:网络整理
导读:目标:我正在 ruby中编写一个工作流命令行程序,它顺序执行UNIX shell上的其他程序,其中一些程序需要用户输入输入. 问题:虽然我可以成功处理stdout和stderr,感谢这个有用的blog post by Nick Charlton,但我仍然坚持捕获用户输入并通过命令行将其传递到子进程
目标:我正在 ruby中编写一个工作流命令行程序,它顺序执行UNIX shell上的其他程序,其中一些程序需要用户输入输入.

问题:虽然我可以成功处理stdout和stderr,感谢这个有用的blog post by Nick Charlton,但我仍然坚持捕获用户输入并通过命令行将其传递到子进程.代码如下:

方法

module CMD
  def run(cmd,&block)
    Open3.popen3(cmd) do |stdin,stdout,stderr,thread|
      Thread.new do # STDOUT
        until (line = stdout.gets).nil? do
          yield nil,line,nil,thread if block_given?
        end
      end

      Thread.new do # STDERR 
        until (line = stderr.gets).nil? do
          yield nil,thread if block_given?
        end
      end

      Thread.new do # STDIN
        # ????? How to handle
      end

      thread.join
    end
  end
end

调用方法

此示例调用shell命令单元,提示用户输入度量单位,然后提示要转换为单位.这就是它在shell中的样子

> units
586 units,56 prefixes        # stdout
You have: 1 litre             # user input
You want: gallons             # user input
* 0.26417205                  # stdout
/ 3.7854118                   # stdout

当我从我的程序运行它时,我希望能够以完全相同的方式与它进行交互.

unix_cmd = 'units'
run unix_cmd do | stdin,thread|
  puts "stdout #{stdout.strip}" if stdout
  puts "stderr #{stderr.strip}" if stderr
  # I'm unsure how I would allow the user to
  # interact with STDIN here?
end

注意:以这种方式调用run方法允许用户能够解析输出,控制流程并添加自定义日志记录.

从我收集的关于STDIN的内容来看,下面的片段与我理解如何处理STDIN的情况一样接近,我的知识中显然存在一些差距,因为我仍然不确定如何将其集成到我上面的run方法中并将输入传递给子进程.

# STDIN: Constant declared in ruby
# stdin: Parameter declared in Open3.popen3
Thread.new do 
    # Read each line from the console
    STDIN.each_line do |line|
       puts "STDIN: #{line}" # print captured input 
       stdin.write line      # write input into stdin
       stdin.sync            # sync the input into the sub process
       break if line == "n"
    end
end

简介:我希望了解如何通过Open3.popen3方法从命令行处理用户输入,以便我可以允许用户将数据输入到从我的程序调用的各种子命令序列中.

解决方法

经过大量关于STDIN的阅读以及一些好的旧试验和错误后,我发现了一个与 Charles Finkel’s answer不同的实现,但有一些细微的差别.

require "open3"

module Cmd
  def run(cmd,thread|
      # We only need to check if the block is provided once
      # rather than every cycle of the loop as we were doing 
      # in the original question.

      if block_given?
        Thread.new do
          until (line = stdout.gets).nil? do
            yield line,thread
          end
        end

        Thread.new do
          until (line = stderr.gets).nil? do
            yield nil,thread
          end
        end
      end

      # $stdin.gets reads from the console
      #
      # stdin.puts writes to child process
      #
      # while thread.alive? means that we keep on
      # reading input until the child process ends
      Thread.new do
        stdin.puts $stdin.gets while thread.alive?
      end

      thread.join
    end
  end
end

include Cmd

像这样调用方法:

run './test_script.sh' do | stdout,thread|
    puts "#{thread.pid} stdout: #{stdout}" if stdout
    puts "#{thread.pid} stderr: #{stderr}" if stderr
  end

test_script.sh的位置如下:

echo "Message to STDOUT"
>&2 echo "Message to STDERR"
echo "enter username: "
read username
echo "enter a greeting"
read greeting
echo "$greeting $username"
exit 0

产生以下成功输出:

25380 stdout: Message to STDOUT
25380 stdout: enter username:
25380 stderr: Message to STDERR
> Wayne
25380 stdout: enter a greeting
> Hello
25380 stdout: Hello Wayne

注意:您会注意到stdout和stderr没有按顺序出现,这是我尚未解决的限制.

如果您有兴趣了解有关stdin的更多信息,那么值得阅读以下问题的答案 – What is the difference between STDIN and $stdin in Ruby?

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读