在`irb`中,反引号抛出异常,但在`rails c`中,它们没有

Jac*_*son 3 ruby shell ruby-on-rails backticks

  • 操作系统:Debian GNU/Linux 8
  • Ruby版本:2.4.1
  • Rails版本:5.1.4

我刚刚rails new创建了一个全新的Rails项目.在其目录中,irb我试图为不存在的可执行文件执行shell命令,并抛出异常:

irb(main):001:0> `foobar`
Errno::ENOENT: No such file or directory - foobar
    from (irb):1:in ``'
    from (irb):1
    from /home/jackson/.rbenv/versions/2.4.1/bin/irb:11:in `<top (required)>'
    from /home/jackson/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.14.6/lib/bundler/cli/exec.rb:74:in `load'
    from /home/jackson/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.14.6/lib/bundler/cli/exec.rb:74:in `kernel_load'
    from /home/jackson/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.14.6/lib/bundler/cli/exec.rb:27:in `run'
    from /home/jackson/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.14.6/lib/bundler/cli.rb:335:in `exec'
    from /home/jackson/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.14.6/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
    from /home/jackson/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.14.6/lib/bundler/vendor/thor/lib/thor/invocation.rb:126:in `invoke_command'
    from /home/jackson/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.14.6/lib/bundler/vendor/thor/lib/thor.rb:359:in `dispatch'
    from /home/jackson/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.14.6/lib/bundler/cli.rb:20:in `dispatch'
    from /home/jackson/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.14.6/lib/bundler/vendor/thor/lib/thor/base.rb:440:in `start'
    from /home/jackson/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.14.6/lib/bundler/cli.rb:11:in `start'
    from /home/jackson/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.14.6/exe/bundle:32:in `block in <top (required)>'
    from /home/jackson/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.14.6/lib/bundler/friendly_errors.rb:121:in `with_friendly_errors'
    from /home/jackson/.rbenv/versions/2.4.1/lib/ruby/gems/2.4.0/gems/bundler-1.14.6/exe/bundle:24:in `<top (required)>'
    from /home/jackson/.rbenv/versions/2.4.1/bin/bundle:22:in `load'
    from /home/jackson/.rbenv/versions/2.4.1/bin/bundle:22:in `<main>'
Run Code Online (Sandbox Code Playgroud)

然而,当我尝试在我的Rails环境中执行相同的操作时rails c,不会抛出异常.反引号返回nil:

irb(main):001:0> `foobar`
rails_console: No such file or directory - foobar
=> nil
Run Code Online (Sandbox Code Playgroud)

这种不一致似乎反映在Rails控制台之外.反引号也会nil在我的Rails应用程序中返回.这是一个问题,因为我们的某个gem中缺少命令的错误处理被破坏,因为它没有抛出异常:https://github.com/dstil/localtunnel/blob/87b1e4b98f600c2a767654caf0a2d94fef5be0e5/lib/localtunnel/client.rb# L37-L41

这种行为的意义是否有意?如果没有,那有什么不对,我该如何解决这个问题呢?

mu *_*ort 5

你的问题似乎是Rails 覆盖反引号以试图标准化行为:

class Object
  # Makes backticks behave (somewhat more) similarly on all platforms.
  # On win32 `nonexistent_command` raises Errno::ENOENT; on Unix, the
  # spawned shell prints a message to stderr and sets $?. We emulate
  # Unix on the former but not the latter.
  def `(command) #:nodoc:
    super
  rescue Errno::ENOENT => e
    STDERR.puts "#$0: #{e}"
  end
end
Run Code Online (Sandbox Code Playgroud)

有责任到处走走:

  • 用于替换核心方法的Rails.
  • 使用反引号的宝石,而不是更安全,更清洁的东西Open3.
  • Ruby没有指定Kernel#`应该如何表现(如果你有足够的意见,甚至可能有反引号).

有办法解决这个问题.您可以尝试在初始化程序中删除ActiveSupport覆盖:

Object.send(:remove_method, :'`')
Run Code Online (Sandbox Code Playgroud)

这样Kernel就可以使用标准的反引号.或者,如果您从未期望拥有lt命令,则可以替换gem的方法:

module LocalTunnel
  class Client
    def self.package_installed?
      false
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

或者可以LocalTunnel::Client.package_installed?通过检查反引号可能失败的各种方式或使用方法Open3而不是反引号来替换您自己的实现,这些实现可以解决Rails覆盖问题.你也可以lv通过分解PATH环境变量(使用File::PATH_SEPARATOR)来手动查找自己File.executable?.

您使用哪种kludge取决于您拥有的可移植性要求以及适用于您的环境的要求.