Runy Open3.popen3 从命令行输入子进程
Runy Open3.popen3 Entering input into the subprocess from the command-line
目标: 我正在 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, nil, line, thread if block_given?
end
end
Thread.new do # STDIN
# ????? How to handle
end
thread.join
end
end
end
调用方法
此示例调用 shell 命令 units
,提示用户输入测量单位,然后提示转换为的单位。这就是它在 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, stdout, stderr, 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
方法处理来自命令行的用户输入,以便我可以允许用户将数据输入各种从我的程序调用的子命令序列。
这里有一些应该有用的东西:
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, nil, line, thread if block_given?
end
end
t = Thread.new { loop { stdin.puts gets } }
thread.join
t.kill
end
end
end
我刚刚在您原来的 run
方法中添加了两行:t = Thread.new { loop { stdin.puts gets } }
和 t.kill
.
在阅读了大量关于 STDIN 的资料以及一些好的旧的试验和错误之后,我发现了一个与 Charles Finkel's 没有什么不同但有一些细微差别的实现。
require "open3"
module Cmd
def run(cmd, &block)
Open3.popen3(cmd) do |stdin, stdout, stderr, 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, nil, thread
end
end
Thread.new do
until (line = stderr.gets).nil? do
yield nil, line, 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, stderr, 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?
目标: 我正在 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, nil, line, thread if block_given?
end
end
Thread.new do # STDIN
# ????? How to handle
end
thread.join
end
end
end
调用方法
此示例调用 shell 命令 units
,提示用户输入测量单位,然后提示转换为的单位。这就是它在 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, stdout, stderr, 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
方法处理来自命令行的用户输入,以便我可以允许用户将数据输入各种从我的程序调用的子命令序列。
这里有一些应该有用的东西:
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, nil, line, thread if block_given?
end
end
t = Thread.new { loop { stdin.puts gets } }
thread.join
t.kill
end
end
end
我刚刚在您原来的 run
方法中添加了两行:t = Thread.new { loop { stdin.puts gets } }
和 t.kill
.
在阅读了大量关于 STDIN 的资料以及一些好的旧的试验和错误之后,我发现了一个与 Charles Finkel's
require "open3"
module Cmd
def run(cmd, &block)
Open3.popen3(cmd) do |stdin, stdout, stderr, 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, nil, thread
end
end
Thread.new do
until (line = stderr.gets).nil? do
yield nil, line, 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, stderr, 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?