2015-04-18 62 views
3

目標:我正在用ruby編寫一個工作流命令行程序,它依次執行UNIX shell上的其他程序,其中一些需要用戶輸入輸入。Runy Open3.popen3從命令行輸入子進程

問題:雖然我可以成功Nick Charlton處理stdoutstderr感謝這個有用blog post,不過,我被困在捕捉用戶輸入,並將其傳遞到子過程通過命令行。的代碼如下:

方法

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 

調用方法

此示例調用外殼命令units它提示用戶輸入的測量單位,然後提示一個單位轉換爲。這是怎麼會看在外殼

> 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,明顯有在我所知一些差距,因爲我仍然不確定如何整合這個放到我上面的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方法的命令行處理用戶輸入,這樣我可以允許用戶將數據輸入到從我的程序稱爲子指令的各種序列。

+0

我也很想知道如何做到這一點 –

回答

1

經過大量閱讀有關STDIN以及一些很好的舊試驗和錯誤,我發現一個實現不與Charles Finkel'sanswer不同,但有一些細微的差異。

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沒有按順序顯示,這是我尚未解決的限制。

如果您想了解更多有關標準輸入這是值得閱讀以下問題的答案 - What is the difference between STDIN and $stdin in Ruby?

0

這裏的東西應該工作:

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

+0

這似乎並沒有工作,我害怕,我通過調用'CMD.run'單元''的方法來測試它,你是否設法使它工作? – ImaginateWayne

+0

是的,這對我有用。請注意,我使用Ruby 2.1.5。 –