Why does Open3.popen3 return wrong error when executable is missing?

℡╲_俬逩灬. 提交于 2019-12-23 21:19:55

问题


I'm making a Ruby wrapper around a CLI. And I found a neat method, Open3.capture3 (which internally uses Open3.popen3), which lets me execute commands and captures stdout, stderr and exit code.

One thing that I want to detect is if the CLI executable wasn't found (and raise a special error for that). I know that the UNIX shell gives exit code 127 when command wasn't found. And when I execute $ foo in bash, I get -bash: foo: command not found, which is exactly the error message I want to display.

With all that in mind, I wrote code like this:

require "open3"

stdout, stderr, status = Open3.capture3(command)
case status.exitstatus
when 0
  return stdout
when 1, 127
  raise MyError, stderr
end

But, when I ran it with command = "foo", I get an error:

Errno::ENOENT: No such file or directory - foo
  /Users/janko/.rbenv/versions/2.1.3/lib/ruby/2.1.0/open3.rb:193:in `spawn'
  /Users/janko/.rbenv/versions/2.1.3/lib/ruby/2.1.0/open3.rb:193:in `popen_run'
  /Users/janko/.rbenv/versions/2.1.3/lib/ruby/2.1.0/open3.rb:93:in `popen3'
  /Users/janko/.rbenv/versions/2.1.3/lib/ruby/2.1.0/open3.rb:252:in `capture3'

Why does this error occur? I thought Open3.capture3 was supposed to execute that command directly in the shell, why then don't I get a normal STDERR and exit code of 127?


回答1:


Open3.popen3 delegates to Kernel.spawn, which depending on the way the command is passed in, gives the command to shell or directly to OS.

commandline                 : command line string which is passed to the standard shell
cmdname, arg1, ...          : command name and one or more arguments (This form does not use the shell. See below for caveats.)
[cmdname, argv0], arg1, ... : command name, argv[0] and zero or more arguments (no shell)

We might expect that if we call Kernel.spawn("foo"), it would be passed to the shell (and not OS). But it doesn't, documentation for Kernel.exec explains why:

If the string from the first form (exec("command")) follows these simple rules:

* no meta characters
* no shell reserved word and no special built-in
* Ruby invokes the command directly without shell

You can force shell invocation by adding ";" to the string (because ";" is a meta character).

Last paragraph reveals the solution.

require "open3"

stdout, stderr, status = Open3.capture3(command + ";")
case status.exitstatus
when 0
  return stdout
when 1, 127
  raise MyError, stderr
end


来源:https://stackoverflow.com/questions/26040249/why-does-open3-popen3-return-wrong-error-when-executable-is-missing

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!