我在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?”的答案。
为什么我会收到此错误,如何解决?
答案 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 Shell和Shellwords。他们是你的朋友。
答案 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)
)。