我正在使用popen4来捕获stdout,stderr和命令行的退出状态。只要我能抓住上述三件事,我就不会受到popen4的束缚。目前我还没有找到捕获命令未找到错误的好方法。我可以在预设的任务中做which cmd
,但希望有内置的东西。
在下面你可以运行一个好的任务,糟糕的任务和一个虚假的任务来查看差异。我正在使用rails new app
gem
popen4
进行此操作
#!/usr/bin/env rake
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require File.expand_path('../config/application', __FILE__)
require 'open4'
# returns exit status 0, all is good
task :convert_good do
puts "convert good"
`wget https://www.google.com/images/srpr/logo3w.png`
status = Open4.popen4("convert logo3w.png output.jpg") do |pid, stdin,stdout,stderr|
stdin.close
puts "stdout:"
stdout.each_line { |line| puts line }
puts "stderr: #{stderr.inspect}"
stderr.each_line { |line| puts line }
end
puts "status: #{status.inspect}"
puts "exit: #{status.exitstatus}"
end
# returns exit status 1, we messed up our command
task :convert_bad do
puts "convert bad"
status = Open4.popen4("convert logo3w-asdfasdf.png output.jpg") do |pid, stdin,stdout,stderr|
stdin.close
puts "stdout:"
stdout.each_line { |line| puts line }
puts "stderr: #{stderr.inspect}"
stderr.each_line { |line| puts line }
end
puts "status: #{status.inspect}"
puts "exit: #{status.exitstatus}"
end
# I want this to return exit code 127 for command not found
task :convert_none do
puts "convert bad"
status = Open4.popen4("convert_not_installed") do |pid, stdin,stdout,stderr|
stdin.close
puts "stdout:"
stdout.each_line { |line| puts line }
puts "stderr: #{stderr.inspect}"
#it doesnt like stderr in this case
#stderr.each_line { |line| puts line }
end
puts "status: #{status.inspect}"
puts "exit: #{status.exitstatus}"
end
以下是3个本地输出
# good
stdout:
stderr: #<IO:fd 11>
status: #<Process::Status: pid 17520 exit 0>
exit: 0
# bad arguments
convert bad
stdout:
stderr: #<IO:fd 11>
convert: unable to open image `logo3w-asdfasdf.png': No such file or directory @ blob.c/OpenBlob/2480.
convert: unable to open file `logo3w-asdfasdf.png' @ png.c/ReadPNGImage/2889.
convert: missing an image filename `output.jpg' @ convert.c/ConvertImageCommand/2800.
status: #<Process::Status: pid 17568 exit 1>
exit: 1
# fake command not found, but returns exit 1 and stderr has no lines
convert bad
stdout:
stderr: #<IO:fd 11>
status: #<Process::Status: pid 17612 exit 1>
exit: 1
答案 0 :(得分:5)
首先要说几点。
你实际上并没有使用 popen4 gem - 它是 open4 gem的包装器(如果你在Unix系统上运行,在至少) - 你直接使用 open4 gem。如果您想使用 popen4 ,您可以这样称呼它:
status = POpen4.popen4('cmd') do |stdout, stderr, stdin, pid|
# ...
end
popen4 方法最终通过内核#exec 方法执行指定的命令,其行为取决于它是否确定给定的命令应该是在shell中运行或不运行。 (你可以看到http://www.ruby-doc.org/core-1.9.3/Kernel.html#method-i-exec,但它并不是非常有用。源代码是一个更好的选择。)
例如:
> fork { exec "wibble" }
=> 1570
> (irb):56:in `exec': No such file or directory - wibble (Errno::ENOENT)
from (irb):56:in `irb_binding'
from (irb):56:in `fork'
from (irb):56:in `irb_binding'
from /Users/evilrich/.rvm/rubies/ree-1.8.7-2011.03/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'
from :0
这里, exec 试图直接执行不存在的命令'wibble' - 因此是例外。
> fork { exec "wibble &2>1" }
=> 1572
> sh: wibble: command not found
在这里, exec 看到我正在使用重定向,因此在shell中执行了我的命令。区别?我在STDERR上遇到错误,也没有例外。您还可以通过在要执行的命令中指定shell来强制使用shell:
> fork { exec "sh -c 'wibble -abc -def'" }
无论如何,了解内核#exec 的行为可能有助于让 popen4 方法按照您希望的方式运行。
要回答你的问题,如果我使用 popen4 gem并以这样的方式构造命令(通过exec的规则)它将在shell中运行或者如果我使用“sh - c ...“在我自己的命令中,然后我得到了我认为你正在寻找的那种行为:
> status = POpen4.popen4("sh -c 'wibble -abc -def'") {|stdout, stderr, stdin, pid| puts "Pid: #{pid}"}
Pid: 1663
=> #<Process::Status: pid=1663,exited(127)>
> puts status.exitstatus
127
<强>更新强>
有趣。 Open4.popen 还会返回127退出状态,如果您从stderr读取。因此,不需要使用 popen gem。
> status = Open4.popen4("sh -c 'wibble -abc -def'") {|pid, stdin, stdout, stderr| stderr.read }
=> #<Process::Status: pid 1704 exit 127>