为什么`eval`比`str2func`更差来评估字符串中的函数?

m79*_*13d 3 matlab eval

我已经表明性能str2func更好,但我得到了很多评论,说明有更多根本原因不使用eval.哪些基本原因适用于以下情况eval并且不适用于str2func以下情况:

f='a^x+exp(b)+sin(c*x)+d'
Run Code Online (Sandbox Code Playgroud)
  1. eval:

    y = eval(f)
    
    Run Code Online (Sandbox Code Playgroud)

    或(rahnema1建议)

    fHandle = eval(['@(x, a, b, c, d) ' f]);
    y = fHandle(x, a, b, c, d);
    
    Run Code Online (Sandbox Code Playgroud)
  2. str2func:

    fHandle = str2func(['@(x, a, b, c, d) ' f]);
    y = fHandle(x, a, b, c, d);
    
    Run Code Online (Sandbox Code Playgroud)

性能原因外,为什么第一个选项比第二个选项更差?

备注

  • 请注意,我知道如果可能的话,最好避免使用这两种方法.

  • 请注意,我将输出分配给eval变量,这样可以避免执行大量棘手的代码.

And*_*eak 10

不要试图淡化安全风险eval.如果您不完全控制输入,则运行任意代码可能会有害.如果您完全控制输入,几乎总有一种更好的方法不涉及动态代码生成.

你坚持一个具体的例子:

>> ls TMPFILE
Error using ls (line 35)
ls: cannot access 'TMPFILE': No such file or directory

>> y = eval('system(''touch TMPFILE'')');
>> y

y =

     0

>> ls TMPFILE
TMPFILE
Run Code Online (Sandbox Code Playgroud)

touch是一个非常友好的unix命令; 它会创建一个空文件.想象一下rm -rf ~在相同的背景下.

让我们尝试一下str2func:

>> y = str2func('system(''touch TMPFILE2'')');
Warning: The input to STR2FUNC "system('touch TMPFILE2')" is not a valid function name. This will generate an error in a future release. 
>> ls TMPFILE2
Error using ls (line 35)
ls: cannot access 'TMPFILE2': No such file or directory

>> y

y =

  function_handle with value:

    @system('touch TMPFILE2')

>> y()
Undefined function or variable 'system('touch TMPFILE2')'.
Run Code Online (Sandbox Code Playgroud)

旁注:虽然风险eval是真实的,而不仅仅是由于迷信,使用eval并不一定意味着你是安全的.我最喜欢的例子,鬼鬼祟祟的魔鬼str2num:

>> x = str2num('[system(''touch TMPFILE3'')]')        

x =

     0

>> ls TMPFILE3
TMPFILE3
Run Code Online (Sandbox Code Playgroud)

所以,即使你没有明确使用eval,它可能发生,你正在使用一个方便的功能,而不是.你应该总是确保不会发生这种情况:使用safe str2double代替str2num,而str2func不是使用eval(因为我们在上面看到它str2func 不会执行任意代码).

  • @ m7913d因为为了工作,攻击者必须有关于程序内部的详细信息.如果你使用`eval`或等价的话,任何`system`命令都会被执行,没有问题.当然,足够复杂的攻击有足够的代码本身信息(!)和程序员(!)的低质量清理工作意味着如果你使用外部输入很难编写防弹代码.但是`str2func`会使[Little Bobby Tables无害](https://xkcd.com/327/). (4认同)
  • @ m7913d我很确定我们已经为你已经没有根据的问题提供了足够的技术答案.没有多少理性推理会说服你不要使用'eval`.我当然不会继续和砖墙说话.您可以随意使用任何您想要的,代码的质量和安全是您的唯一责任. (3认同)

And*_*uri 9

首先,性能(特别是x100较慢)应该是不使用某些东西的充分理由.

但是,你错过了这一点.您现在问"为什么不在eval评估字符串中的函数的具体示例中使用?".那么,答案就是因为你有一个函数被称为str2func专门更快,更安全地完成这项工作.你不应该使用eval的原因是因为在你想要使用eval的情况下,代码的逻辑是有缺陷的.

唯一的原因eval是评估一个不仅仅是字符串函数的任意输入(为什么你会这样做,你已经表明它有一个特定的函数).如果你知道你在评估什么,那么你不需要eval,你可以编写你期望的代码.因此,eval仅在您接受通用输入时使用.但通用输入包括rm -rf删除整个操作系统.或者在一个不那么灾难性的情况下,代码可能会重写一个对算法其余部分很重要的变量.很明显,为什么你不想让代码运行任意输入.

动态变量怎么样?一个可以使用生成的可怕想法eval.你可能会通过接受任意输入而意外地做到这一点.

但还有更多的事情.eval确实使你的代码不可读.在运行时之前,您不知道代码的作用.

我见过在其核心功能中执行此操作的代码

model.solve_algorithm=eval(['default(',[ class(input3) ],')']);
result=eval(model.solve_algorithm);
Run Code Online (Sandbox Code Playgroud)

代码做了什么?有什么选择?没有办法知道,除非你运行它,看看它去了哪里.这使代码变得模糊,难以阅读,并且当然难以维护.在代码中明确表示在代码的可维护性方面确实受益匪浅.

TLDR:在任何情况下,您可能想要使用eval,其中一个正在发生:

  1. 对于您想要做的事情,有一个更好,更具体的功能
  2. 你真的不应该做你想做的事情,代码/逻辑需要重组.

  • 这不是一个很好的类比,因为很难找到"使用'eval` well"的例子 (3认同)
  • @ m7913d我要在此回应安德拉斯.在我的回答中有足够的理由,在第二段中的答案是"你错过了重点".你仍然缺少这一点.重新阅读这个问题,因为它清楚地解释了大局,而不仅仅是你试图捍卫的樱桃选择的例子. (2认同)
  • @ m7913d你错过了这一点.您正在寻找一个具体的例子*在您提供的具体示例*中.eval不安全的一个具体例子是`eval(whatever_users_says)`.樱桃选择使用eval是安全的情况很容易.为什么不在'eval中使用eval(['a = randi(',input,')'])`?它永远是安全的!但是,如果你只看这个例子,你就错过了这一点 (2认同)

Dev*_*-iL 8

TL; DR - eval可以访问本地定义的函数,而str2func不是.在这两者中使用的"正确"功能取决于代码的结构.


在没有深入eval讨论整个"为什么是坏"的讨论的情况下,我将尝试着重于在您的问题的上下文中讨论它的相关MATLAB文档:

考虑一个包含两个函数的文件:

function out = q46213509
  f='a^x+exp(b)+sin(c*x)+d';
  fHandle = {eval(['@(x, a, b, c, d) ' f]), str2func(['@(x, a, b, c, d) ' f])};
  out = [fHandle{1}(1,2,3,4,5) fHandle{2}(1,2,3,4,5)];
end

function e = exp(x)
  e = x.^0./factorial(0) - x.^1./factorial(1) + x.^2./factorial(2); % Error is deliberate
end
Run Code Online (Sandbox Code Playgroud)

当您运行上述操作时,会导致:

ans =

    8.7432   26.3287
Run Code Online (Sandbox Code Playgroud)

如果您正在使用类并且您定义了自己的运算符,这可能会失控...假设某人决定将文件添加到您的MATLAB路径中,并方便地为其指定您使用的某个函数的名称,或者一个可重载的运算符(即mpower.m):

function out = mpower(varargin)
  % This function disregards all inputs and prints info about the calling workspace.
  disp(struct2cell(evalin('caller','whos')));
end
Run Code Online (Sandbox Code Playgroud)

虽然在某些情况下,MATLAB可以防止重新定义内置函数,但我敢打赌,上面的场景可能会str2func让人感到困惑......


Wol*_*fie 8

我暂时搁置两种方法的论点,尽管它们都非常有效,并且尽责的读者应该尝试理解它们.


我可以看到两个明显的差异.对于这些示例,我们必须假设您已经使用一些可变数据构造了脚本中函数的输入字符串,并假设它可能是任何内容(错误或其他).所以我们准备并解决最坏的情况.

  1. str2func包括一些额外的检查,以确保您传递的是一个有效的功能,这可能会避免不必要的行为.让我们不要采取"是的但你可以这样做以避免这种情况"的立场,并看一个例子......

    % Not assigning an output variable
    % STR2FUNC: You've harmlessly assigned ans to some junk function
    str2func('delete test.txt') 
    % EVAL: You've just deleted your super important document
    eval('delete test.txt')     
    
    % Assigning an output variable
    % STR2FUNC: You get a clear warning that this is not a valid function
    f = str2func('delete test.txt') 
    % EVAL: You can a non-descript error "Unexpected MATLAB expression"
    f = eval('delete test.txt')
    
    Run Code Online (Sandbox Code Playgroud)
  2. 另一个区别是大约一半str2func文档的主题.它涉及变量范围,可在"检查str2func和之间的差异eval" 标题下找到.

    [函数简介]
    如果使用匿名函数的字符向量表示,则生成的函数句柄无法访问私有或本地函数.

    [检查之间的差异str2funceval]
    str2func一个与表示对匿名函数的特征向量使用,它没有访问本地函数[...].该eval函数可以访问本地函数.


总而言之,您可能有一些用例,其中每个函数都是可取的,具体取决于您所需的错误处理和变量作用域应该永远不会使用evalstr2func尽可能使用,如其他答案中所述.

  • 是的,首选行为取决于用户,这就是为什么我没有说我更喜欢功能而不是另一个.但这适用于几乎*任何*编程功能.这只是两个非常相似的函数的不同行为的比较.这里的意见是"两者都不好",事实是我最后一句话以外的一切. (3认同)
  • 我认为路易斯实际上暗示的是"请在你的回答中提到`eval`是不可取的,原因比表现更为根本,因为`str2func`即使它更快".虽然我不能代替他,但是很容易解雇`eval`,'str2func`不太常见,所以不会很快被解雇(虽然它可能出于大多数相同的原因,但如上所述有*更多*更多保护) (3认同)
  • @ m7913d是的,我的意思正是Wolfie在上面的评论中所说的:不要使用`eval`,因为它可能会导致问题.我真的没有和`str2func`比较.我现在看到我的评论可能会让你感到困惑(对不起!),因为你的问题是关于比较两者 (2认同)