Open3.popen3在Windows上返回错误的错误Errno :: ENOENT

时间:2016-08-18 17:50:41

标签: ruby ruby-2.2 popen3

我在test.rb中有以下代码:

require 'open3'
cmd = 'C:\Program Files\foo\bar.exe'
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
  puts "stdout: #{stdout.read}"
  puts "\n\n"
  puts "stderr: #{stderr.read}"
end

bar.exe是我创建的控制台应用程序,位于C:\Program Files\foo\。当我运行bar.exe时:

  • 输出"Hello world!"
  • 使用任何参数,例如bar.exe /blah,它会输出帮助信息。

当我运行ruby test.rb时,我收到此错误:

C:\RailsInstaller/Ruby2.2.0/lib/ruby/2.2.0/open3.rb:193:in 'spawn': No such file or directory - C:\Program Files\foo\bar.exe (Errno::ENOENT)
from C:\RailsInstaller/Ruby2.2.0/lib/ruby/2.2.0/open3.rb:193:in 'popen_run'
from C:\RailsInstaller/Ruby2.2.0/lib/ruby/2.2.0/open3.rb:193:in 'popen3'
from test.rb:3:in '<main>'

如果我更改代码以致电popen3

Open3.popen3(cmd, '')

我没有收到Errno::ENOENT错误,而是收到了帮助消息,但我想要"Hello World"输出。

我搜索了一个解决方案但没有任何工作,包括“Why does Open3.popen3 return wrong error when executable is missing?”的答案。

为什么我会收到此错误,如何解决?

3 个答案:

答案 0 :(得分:1)

默想:

cmd = "\P\f\b"
cmd.size             # => 3
cmd.chars            # => ["P", "\f", "\b"]
cmd.chars.map(&:ord) # => [80, 12, 8]

cmd = "\\P\\f\\b"
cmd.size             # => 6
cmd.chars            # => ["\\", "P", "\\", "f", "\\", "b"]
cmd.chars.map(&:ord) # => [92, 80, 92, 102, 92, 98]

cmd = '\P\f\b'
cmd.size             # => 6
cmd.chars            # => ["\\", "P", "\\", "f", "\\", "b"]
cmd.chars.map(&:ord) # => [92, 80, 92, 102, 92, 98]

您使用带有单个反斜杠的双引号字符串作为路径/目录分隔符,如第一个示例中所示。单个反斜杠\f\b是双引号字符串中的转义字符,无法识别,因为它们是使用 \ f 或 \ b

有两种方法可以解决这个问题,或者像第二个例子中那样转义反斜杠,或者使用单引号字符串,如第三个示例所示。使用第二种方法被认为是混乱的,因此使用最后一种方法是为了便于阅读和维护。您可以获得相同的角色,同时减少视觉噪音。这适用于大多数语言中的字符串使用。

要知道的第二件事是Ruby不需要反斜杠作为路径分隔符。 IO documentation说:

  如果可能,Ruby将在不同的操作系统约定之间转换路径名。例如,在Windows系统上,文件名"/gumby/ruby/test.rb"将以"\gumby\ruby\test.rb"打开。在Ruby字符串中指定Windows样式的文件名时,请记住转义反斜杠:

     

"c:\\gumby\\ruby\\test.rb"

     

我们这里的例子将使用Unix风格的正斜杠; File :: ALT_SEPARATOR可用于获取特定于平台的分隔符。

最后,您应该查看STDLib中的Ruby ShellShellwords。他们是你的朋友。

答案 1 :(得分:0)

您遇到了麻烦,因为“Program Files”是一个包含空格的文件夹。每当发生这种情况时,您需要对其进行双引号,就像在cmd.exe提示符上一样。当你进行双引号时,你必须记住你的反斜杠字符“\”是一个转义字符,所以你必须使用双反斜杠来获得适当的Windows文件夹分隔符。我将使用实际在我的环境中返回内容的代码;根据您的口味调整它。所以你的代码应该是这样的:

require 'open3'
cmd = "\"C:\\Program Files\\Git\\bin\\git.exe\""
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
  puts "stdout: #{stdout.read}"
  puts "\n\n"
  puts "stderr: #{stderr.read}"
end

如果你有命令行参数传递给git,你可以这样做:

require 'open3'
cmd = "\"C:\\Program Files\\Git\\bin\\git.exe\" --version"
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
  puts "stdout: #{stdout.read}"
  puts "\n\n"
  puts "stderr: #{stderr.read}"
end

答案 2 :(得分:0)

使用Open3.popen3([bin, bin])来阻止对popen3的单个参数(以及spawn等相关方法)的shell命令处理。

正如您所注意到的,可以在不调用shell的情况下正常传递多个args(例如 Open3.popen3(bin, arg1, arg2))。