为什么应避免使用exec()和eval()?

46 投票
11 回答
21616 浏览
提问于 2025-04-15 17:12

我在很多地方都看到过这个问题,但从来没有找到一个令人满意的解释,为什么我们应该这样做。

所以,希望这里能给出一个答案。为什么我们(至少一般来说)不应该使用 exec()eval() 呢?

补充说明:我注意到大家都在假设这个问题和网络服务器有关——其实并不是。我能理解如果把一个未经处理的字符串传给 exec 可能会有问题。那么在非网络应用中,这样做也会有问题吗?

11 个回答

12

抛开安全问题不谈,evalexec 通常被认为是不太好的选择,因为它们会让代码变得复杂。当你看到 eval 被调用时,往往不知道它背后到底发生了什么,因为它是对一个通常存储在变量中的数据进行操作。这让代码变得不容易理解。

使用解释器的全部功能就像是一种强大的武器,应该只在非常棘手的情况下使用。不过在大多数情况下,最好还是避免使用它们,选择一些更简单的工具。

不过,像所有的概括一样,这个观点也要谨慎对待。在某些情况下,execeval 可能会很有用。但你必须有非常充分的理由去使用它们。想了解一个可以接受的用法,可以参考 这篇文章

17

当你需要使用exec和eval时,确实是有必要的。

但是,很多人使用这些函数(还有其他脚本语言中的类似功能)其实是不合适的,完全可以用更简单的方法来替代,这样不仅更快,而且更安全,出错的可能性也更小。

你是可以在正确处理字符串的情况下安全地使用exec和eval的。但是,那些直接用exec/eval来解决问题的程序员(因为他们不理解语言提供的其他功能)往往无法正确处理这些操作;他们可能对字符串处理一无所知,只是一味地拼接字符串,这样就会导致代码脆弱且不安全。

这就是“字符串的诱惑”。随意处理字符串片段看起来很简单,容易让初学者误以为自己懂得很透彻。但经验告诉我们,结果几乎总是在某个边缘情况(或不那么边缘的情况)出错,往往还会带来安全隐患。这就是我们说eval是“邪恶”的原因。这也是我们说用正则表达式处理HTML是“邪恶”的原因。这也是我们提倡SQL参数化的原因。没错,你确实可以通过手动处理字符串来做到这些……但除非你已经理解了我们为什么这么说,否则你很可能做不到。

32

有时候,有更简单、更直接的方法可以达到相同的效果。如果你构建一个复杂的字符串并传给exec,这段代码就会变得难以理解,也难以测试。

举个例子:我写了一段代码,它读取字符串的键和值,然后把对应的字段设置到一个对象里。代码大概是这样的:

for key, val in values:
    fieldName = valueToFieldName[key]
    fieldType = fieldNameToType[fieldName]
    if fieldType is int:
        s = 'object.%s = int(%s)' % (fieldName, fieldType) 
    #Many clauses like this...

exec(s)

这段代码在简单的情况下还算可以,但随着新类型的出现,它变得越来越复杂。当出现错误时,错误总是在调用exec的时候触发,所以堆栈跟踪并不能帮助我找到问题。最后,我改用了一个稍微长一点、但更清晰的版本,明确地设置每个字段。

代码清晰的第一条规则是:你代码的每一行都应该通过查看附近的几行就能容易理解。这就是为什么不鼓励使用goto和全局变量的原因。exec和eval很容易让你严重违反这个规则。

撰写回答