我使用的是:
- Ruby 1.9.3-p448
- Windows Server 2008
我有一个包含程序使用的命令的文件,我以这种方式使用
C:> PATH_TO_FOLDER/program.exe file.txt
File.txt
有一些命令,因此program.exe
将执行以下操作:
- Execute commands
- Reads from a DB using an ODBC method used by program
- Outputs result in a txt file
使用powershell,此命令运行良好,如预期。
现在我有一个文件(app.rb
):
require 'sinatra'
require 'open3'
get '/process' do
program_path = "path to program.exe"
file_name = "file.txt"
Open3.popen3(program_path, file_name) do |i, o, e, w|
# I have some commands here to execute but just as an example I'm using o.read
puts o.read
end
end
现在,当通过访问http://localhost/process
来使用它时,Open3
通过这样做来工作(我不能100%确定,但在尝试了几次之后,我认为这是唯一的选择)
读取命令并执行它们(这是可以的)
尝试使用ODBC方法读取数据库(这是我的问题需要从
Open3
接收一些输出,这样我就可以在浏览器中显示它,但我想当它试图读取时,它会启动Open3
不知道的另一个进程,所以Open3会继续并完成而不等待它)退出
退出
我发现了以下内容:
- 使用
Thread.join
(在本例中为w.join
)以等待进程完成,但它不起作用 Open4
似乎可以处理子进程,但在Windows上不起作用Process.wait(pid)
,在本例中为pid = w.pid
,但也不起作用Timeout.timeout(n)
,这里的问题是我不确定要持续多久需要吗
有什么办法处理这个问题吗?(等待Open3
子流程,以便获得正确的输出)。
我们在获取退出状态时遇到了类似的问题,这就是我们所做的
Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thr|
# print stdout and stderr as it comes in
threads = [stdout, stderr].collect do |output|
Thread.new do
while ((line = output.gets rescue '') != nil) do
unless line.blank?
puts line
end
end
end
end
# get exit code as a Process::Status object
process_status = wait_thr.value #.exitstatus
# wait for logging threads to finish before continuing
# so we don't lose any logging output
threads.each(&:join)
# wait up to 5 minutes to make sure the process has really exited
Timeout::timeout(300) do
while !process_status.exited?
sleep(1)
end
end rescue nil
process_status.exitstatus.to_i
end
只有在琐碎的情况下使用Open3.popen3
才很容易。我不知道处理子流程的输入、输出和错误通道的真实代码。我也不知道您的子流程的确切行为:它是在stdout上写的吗?它写在stderr上吗?它是否尝试从stdin读取?
这就是为什么我认为您用puts o.read
替换的代码中存在问题。关于您可能遇到的问题,请访问http://coldattic.info/shvedsky/pro/blogs/a-foo-walks-into-a-bar/posts/63.
尽管我不同意这篇文章的作者Pavel Shved在寻找解决方案方面的观点。他提出了自己的解决方案。我只是在我的项目中使用popen3
的一个包装函数:Open3.capture*
。他们做所有困难的事情,比如同时等待stdout和stderr。