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的主要内容,如果未能解决你的问题,请参考以下文章