Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/vba/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Excel 如何在VBA for Mac中加速shell函数_Excel_Vba_Macos_Shell_Curl - Fatal编程技术网

Excel 如何在VBA for Mac中加速shell函数

Excel 如何在VBA for Mac中加速shell函数,excel,vba,macos,shell,curl,Excel,Vba,Macos,Shell,Curl,我一直在使用问题顶部答案中的函数在VBA(execShell函数)中运行shell脚本,该函数使用libc.dylib: 函数中有一个while循环,它的运行速度比popen慢得多。我猜这个部分是在shell中执行的,popen正在初始化,但我不太明白它在做什么 Time taken to run popen: 0.01171875 seconds Time taken to run while loop: 0.8710938 seconds 我想知道如果函数需要很长时间才能执行,是否可以中

我一直在使用问题顶部答案中的函数在VBA(execShell函数)中运行shell脚本,该函数使用libc.dylib:

函数中有一个while循环,它的运行速度比popen慢得多。我猜这个部分是在shell中执行的,popen正在初始化,但我不太明白它在做什么

Time taken to run popen:
0.01171875 seconds
Time taken to run while loop:
0.8710938 seconds
我想知道如果函数需要很长时间才能执行,是否可以中止或超时该函数?是否有一种方法可用于处理多个函数调用?我在shell脚本中为curl命令添加了timeout<代码>“curl--连接超时5--最长时间10--重试2--重试延迟0--重试最长时间40”,并且在每次调用之前增加了0.1秒的延迟。如果它运行顺利,这不会是一个问题,但有时它似乎可以完全锁定。如果curl请求有很长的延迟,我宁愿中止它并继续脚本。可能在连续几次失败后,脚本会完全终止。我原以为函数调用将以线性方式处理,但我认为它可能会同时处理shell脚本,这可能会导致问题。理想情况下,我希望得到关于脚本状态的反馈,我尝试使用状态栏实现脚本,但由于2011版Excel的限制,这不起作用。在运行开始时,我可以看到工作表正在更新,一切都在顺利运行,但稍后在脚本中excel会冻结,直到完成

另一个解决方案是使用MacScript命令,但是这个shell脚本包含带有“\”和多个引号的regex,并且很难转换为applescript,因此我首选使用这个方法,因为它已经可以工作了。使用system()命令不是选项,因为VBA脚本需要输出

    Private Declare Function popen Lib "libc.dylib" (ByVal command As String, ByVal mode As String) As Long
    Private Declare Function pclose Lib "libc.dylib" (ByVal file As Long) As Long
    Private Declare Function fread Lib "libc.dylib" (ByVal outStr As String, ByVal size As Long, ByVal items As Long, ByVal stream As Long) As Long
    Private Declare Function feof Lib "libc.dylib" (ByVal file As Long) As Long

    Function execShell(command As String, Optional ByRef exitCode As Long) As String
        Dim file As Long
        file = popen(command, "r")

        If file = 0 Then
            Exit Function
        End If

        While feof(file) = 0
            Dim chunk As String
            Dim read As Long
            chunk = Space(50)
            read = fread(chunk, 1, Len(chunk) - 1, file)
            If read > 0 Then
                chunk = Left$(chunk, read)
                execShell = execShell & chunk
            End If
        Wend

        exitCode = pclose(file)
    End Function

popen
运行速度更快,因为它是用C/C++编写的,并且是二进制可执行文件。它在操作系统级别运行,而不是在VBA虚拟机(VBA VM)中运行。您的While循环和VBA中的所有内容都是在VBA-VM中运行的脚本语言,它必须将您的脚本转换为p代码,然后由VBA-VM执行。如果你感兴趣,维基百科上的文章会提供更详细的解释

VBA代码总是比Excel对象或外部函数(如
popen
)提供的任何方法都慢。例如,在使用值循环填充范围时,使用范围对象自动填充方法始终比VBA快

向代码中添加“超时”的最佳方法是在while循环中添加VBA方法。然后

我会把它放在
End If
Wend
之间,如下所示:

        End If
    VBA.DoEvents
Wend
Application.ScreenUpdating = False
While feof(file) = 0 
.
.
.
Wend
Application.ScreenUpdating = True
这将导致
While
循环允许Excel和Windows处理事件。除了VBA
Shell()
函数外,VBA代码是串行执行的。如果没有
VBA.DoEvents
,代码将继续执行,直到完成或出错。这提供了一个很好的解释

可以加快代码速度的一件事是使用以下命令关闭屏幕更新
Application.ScreenUpdating=False
。请记住,使用
Application.ScreenUpdating=True
在子菜单的末尾将其重新打开。我会把它放在
周围,而
循环如下:

        End If
    VBA.DoEvents
Wend
Application.ScreenUpdating = False
While feof(file) = 0 
.
.
.
Wend
Application.ScreenUpdating = True

另外,如果你真的很喜欢冒险,你可以编写一个事件处理程序。Chip Pearson有一篇很棒的文章。

感谢您提供了信息丰富的答案。现在,我已将DoEvents添加到循环中。在进一步调试之后,我发现问题不是我的脚本太慢,而是服务器在一段时间后阻塞了请求,几分钟后块被删除,然后脚本继续。我已经将等待时间从0.1秒改为0.5秒,解决了这个问题,但是如果curl超时,我需要为它设置一个错误。我还将连接超时时间从5秒改为5秒,因为这对于某些请求来说是不够的,但对于大多数请求来说,连接超时时间不到1秒。