ruby “eval”应该是下流的吗?

whitzsjs  于 2023-11-18  发布在  Ruby
关注(0)|答案(8)|浏览(155)

我已经在ruby中使用eval方法很多次了。但是我听到有人说eval很讨厌。当被问到为什么以及如何使用时,我从来没有得到一个令人信服的理由不使用它。它们真的很讨厌吗?如果是这样,在什么方面?有什么可能的“更安全”的选择来评估?

chhqkbe1

chhqkbe11#

如果你正在eval处理一个由用户提交或可由用户修改的字符串,这相当于允许任意代码执行。想象一下,如果字符串包含对rm -rf /或类似的操作系统调用。也就是说,在你知道字符串被适当约束的情况下,或者你的Ruby解释器被适当地沙箱化,或者理想情况下,eval可以非常强大。
这个问题类似于SQL injection,如果你熟悉的话。这里的解决方案类似于注入问题的解决方案(参数化查询)。也就是说,如果你想eval的语句是一个非常特定的形式,而不是所有的语句需要由用户提交,只有几个变量,一个数学表达式,或类似的,您可以从用户那里获取这些小片段,如果需要的话对它们进行清理,然后使用插入到适当位置的用户输入来评估安全模板语句。

5t7ly7z5

5t7ly7z52#

eval不仅不安全(正如在其他地方指出的那样),它也很慢。每次执行它,都需要解析eval ed代码的AST(对于JRuby,则转向字节码),这是一个字符串繁重的操作,而且可能对缓存局部性也不好(假设一个运行中的程序并不需要很多eval,因此解释器的相应部分除了很大之外,也是缓存冷的)。
你会问,为什么Ruby中会有eval?“因为我们可以”--事实上,当eval被发明的时候,(对于LISP编程语言),它主要是为了展示!更重要的是,当你想“在你的解释器中添加一个解释器”时,使用eval是正确的,对于元编程任务,比如写一个预处理器,调试器或模板引擎。这类应用程序的共同想法是修改一些Ruby代码并在其上调用eval,它肯定胜过重新发明和实现特定于域的玩具语言,这一陷阱也被称为Greenspun's Tenth Rule。警告是:注意成本,例如模板引擎,在启动时而不是运行时执行所有的eval操作;不要eval不受信任的代码,除非你知道如何“驯服”它,ie根据capability discipline的理论选择并强制执行语言的安全子集。后者是一个非常困难的工作(参见例如how that was done for Java;不幸的是,我不知道Ruby有任何这样的努力)。

2hh7jdfx

2hh7jdfx3#

在Ruby中,有几个噱头可能比eval()更合适:
1.有一个#send,它允许你调用一个方法,它的名字是字符串,并向它传递参数。

  1. yield允许你传递一个代码块给一个方法,这个方法将在接收方法的上下文中执行。
    1.通常,简单的Kernel.const_get("String")就足以获取名称为字符串的类。
    我想我不能详细解释它们,所以我只是给你提示,如果你感兴趣,你会谷歌。
3hvapo4f

3hvapo4f4#

它使调试变得困难,使优化变得困难,但最重要的是,它通常是一个信号,表明有一个更好的方法来做你想做的事情。
如果你告诉我们你想用eval完成什么,你可能会得到一些与你的特定场景相关的答案。

vptzau2j

vptzau2j5#

Eval是一个非常强大的特性,应该小心使用。除了Matt J指出的安全问题外,你还会发现调试运行时评估的代码是非常困难的。运行时评估的代码块中的问题对于解释器来说很难表达-所以寻找它会很困难。
话虽如此,如果你对这个问题感到满意,并且不担心安全问题,那么你不应该避免使用使ruby如此吸引人的特性之一。

2lpgd968

2lpgd9686#

在某些情况下,一个放置良好的eval是聪明的,并减少了所需的代码量。除了Matt J提到的安全问题外,您还需要问自己一个非常简单的问题:
当一切都说了和做了,其他人能读你的代码和理解你做了什么吗?
如果答案是否定的,那么您从eval中获得的好处就被可维护性所抛弃了。这个问题不仅适用于您在团队中工作的情况,而且也适用于您-您希望能够在几个月(如果不是几年)后回顾您的代码,并知道您做了什么。

nzkunb0c

nzkunb0c7#

eval方法本身并没有什么问题,但是,要求使用它通常是架构设计和设计模式决策薄弱的标志。
我作为一名Ruby开发人员已经全职工作了12年,我从来没有在我的任何存储库中使用过eval,但是当我在现有的存储库中发现它时,它总是绕过已经存在的复杂性。
它还带来了巨大的安全风险,因为任何动态值都是使用应用程序用户帐户针对实际服务器进行评估的。

0ejtzxu1

0ejtzxu18#

如果你把从“外部”得到的任何东西传递给eval,你就做错了,而且这是非常糟糕的。很难让代码足够安全,所以我认为这是非常不安全的。但是,如果你使用eval来避免重复或其他类似的事情,比如下面的代码示例,使用它是可以的。

class Foo
  def self.define_getters(*symbols)
    symbols.each do |symbol|
      eval "def #{symbol}; @#{symbol}; end"
    end
  end

  define_getters :foo, :bar, :baz
end

字符串
然而,至少在Ruby 1.9.1中,Ruby有非常强大的元编程方法,你可以这样做:

class Foo
  def self.define_getters(*symbols)
    symbols.each do |symbol|
      define_method(symbol) { instance_variable_get(symbol) }
    end
  end

  define_getters :foo, :bar, :baz
end


在大多数情况下,您希望使用这些方法,并且不需要转义。
eval的另一个缺点是(至少在Ruby中),它相当慢,因为解释器需要解析字符串,然后在当前绑定中执行代码。其他方法直接调用C函数,因此你应该得到相当大的速度提升。

相关问题