2013-11-25 26 views
0

我寫執行shell命令的功能,並返回其退出代碼,stdout和stderr。使用popen3無法正確處理STDOUT和STDERR輸出?

問題是,此函數無法正確捕獲STDOUT和STDERR輸出。

def sh(*args) 
    options = args[-1].respond_to?(:to_hash) ? args.pop.to_hash: {} 
    options = { :timeout => 0, :sudo => false }.merge(options) 
    cmd = options[:sudo] == false ? args[0] : "sudo " << args[0] 

    begin 
    stdin, stdout, stderr, wait_thr = Open3.popen3(cmd) 
    pid = wait_thr[:pid] 
    out_buf = "" 
    err_buf = "" 
    start = Time.now 

    # Manually ping the process per 0.2 second to check whether the process is alive or not 
    begin 
     out_buf << stdout.read_nonblock(4096) 
     err_buf << stderr.read_nonblock(4096) 
     # kill the process if it timeouts 
     if options[:timeout] != 0 && (Time.now - start) > options[:timeout] 
     Process.kill("KILL", pid) 
     Process.detach(pid) 
     raise RuntimeError, "process with pid #{pid} timed out with #{options[:timeout]} seconds." 
     end 
     sleep 0.2 
    rescue IO::WaitReadable, EOFError 
    end while wait_thr.alive? 

    rescue => e 
    NtfLogger.warn("sh '#{args}' executed with failure: #{e}") 

    ensure 
    if wait_thr.nil? 
     return 1, out_buf, err_buf 
    else 
     return wait_thr.value.exitstatus, out_buf, err_buf 
    end 
    end 
end # end of sh 

請問有人能幫我弄清楚是什麼問題?

+0

你是什麼意思「無法捕捉」在這裏嗎?它超時嗎?什麼? – rogerdpack

+0

我的意思是它經常從stdout和stderr丟失一些文本。 – TieDad

回答

0

我的popen3「瞭解也是文檔是它的更好的一個區塊內做處理:

Open3.popen3([env,] cmd... [, opts]) do |stdin, stdout, stderr, wait_thr| 
    pid = wait_thr.pid # pid of the started process. 
    ... 
    exit_status = wait_thr.value # Process::Status object returned. 
end 

在非塊的形式,該文檔注意,流必須被關閉:

stdin, stdout, stderr, wait_thr = Open3.popen3([env,] cmd... [, opts]) 
pid = wait_thr[:pid] # pid of the started process. 
... 
stdin.close # stdin, stdout and stderr should be closed explicitly in this form. 
stdout.close 
stderr.close 
exit_status = wait_thr.value # Process::Status object returned. 

http://www.ruby-doc.org/stdlib-2.0.0/libdoc/open3/rdoc/Open3.html#method-c-popen3

最後,FWIW,這裏的周圍capture3,我對我的最終使用的包裝。您可以輕鬆地擴展它來添加一個sudo選項,以防您sh實用程序的線程相關的部分不是關鍵:

# 
# Identical to Open3.capture3, except that it rescues runtime errors 
# 
# @param env optional (as `Kernel.system') 
# @param *cmd the command and its (auto-escaped) arguments 
# @param opts optional a hash of options (as `Kernel.system') 
# 
# @return [stdout, stderr, success] | [$/, $/, nil] on error 
# 
def system3(*cmd) 
    begin 
    stdout, stderr, status = Open3.capture3(*cmd) 
    [stdout, stderr, status.success?] 
    rescue 
    [$/, $/, nil] 
    end 
end 
+0

我的包裝的一個重要特性是檢查執行時間限制。如果我的命令不能在一定限度內完成,我會殺死它。但'system3'包裝器沒有這個功能。 – TieDad

+0

@EvanLi:是的。根據我自己的評論:「如果您的sh實用程序的線程相關部分不重要」...在這種情況下,我的理解是您在實際使用和閱讀任何內容之前必須關閉這些流。 –

+0

這是真的嗎?我嘗試過'out_buf << stdout.read_nonblock(4096)'關閉標準輸入,輸出和錯誤,但我得到不同的是'關閉stream'。 – TieDad