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

Posted

技术标签:

【中文标题】Open3.popen3 在 Windows 上返回错误错误 Errno::ENOENT【英文标题】:Open3.popen3 returns wrong error Errno::ENOENT on Windows 【发布时间】:2016-12-25 18:16:57 【问题描述】:

我在 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?”的答案。

为什么会出现此错误以及如何解决?

【问题讨论】:

'C:/Program Files/foo/bar.exe' 可以替代吗? 【参考方案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 的 Shell 和 Shellwords。他们是你的朋友。

【讨论】:

非常感谢您的详细回答。我首先使用了单引号。但它没有用。奇怪的行为!按照 Jay Godse 的建议,它奏效了。 转义空格的建议是有效的。使用转义反斜杠的建议只会导致噪音。 我完全同意。奇怪的是Open3.popen3('C:\Program Files\foo\bar.exe') 给出了一个错误,而Open3.popen3("\"C:\\Program Files\\foo\\bar.exe\"") 工作正常。【参考方案2】:

您遇到了麻烦,因为“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

【讨论】:

不要在双引号字符串中使用转义转义符作为路径。而是做简单的事情,让 Ruby 找出路径。 Ruby 将自动正确处理 Windows 上的正斜杠并正确转换它们。有关更多信息,请参阅IO documentation 的开头部分。 大约 25 年前,我曾在 BNR 与一个绰号为“铁皮人”的人一起工作。是你,你是约翰吗? 不。那会是其他人。【参考方案3】:

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

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

【讨论】:

以上是关于Open3.popen3 在 Windows 上返回错误错误 Errno::ENOENT的主要内容,如果未能解决你的问题,请参考以下文章

在 Windows 8.1 上运行 Windows 10 通用应用

如何在Windows环境下配置Caffe

如何在Linux上运行Windows应用程序

如何在windows里面安装MySQL

spark可以在windows下运行吗

如何使用 WCF 在 Windows 服务和 Windows 窗体之间进行通信?