2

我在 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"输出。

我搜索了一个解决方案,但没有任何效果,包括“为什么 Open3.popen3 在可执行文件丢失时返回错误错误? ”的答案。

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

4

3 回答 3

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是双引号字符串中的转义字符,并且无法识别,因为它们是使用\ for键入的\ b

你有两种方法来处理这个问题,或者像第二个例子那样转义反斜杠,或者像第三个例子那样使用单引号字符串。使用第二种方式被认为是混乱的,所以使用最后一种方式是为了可读性和更容易维护。你会得到相同的字符,但视觉噪音更少。这适用于大多数语言中的字符串使用。

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

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

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

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

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

于 2016-08-18T19:20:11.893 回答
0

您遇到了麻烦,因为“程序文件”是一个包含空格的文件夹。每当发生这种情况时,您都需要双引号,就像在 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
于 2016-08-18T18:24:37.810 回答
0

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

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

于 2017-01-27T20:41:44.430 回答