我正在编写一个执行shell命令的函数,并返回其退出代码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 :(得分: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