如何在VBA for Mac中加速shell功能

问题描述 投票:3回答:1

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

VBA Shell function in Office 2011 for Mac

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

Time taken to run popen:
0.01171875 seconds
Time taken to run while loop:
0.8710938 seconds

我想知道如果需要很长时间才能执行,是否可以中止或超时功能?有没有可用于处理多个函数调用的方法?我在shell脚本中为curl命令添加了超时。 "curl --connect-timeout 5 --max-time 10 --retry 2 --retry-delay 0 --retry-max-time 40 ",每次通话前还加0.1秒延迟。如果它顺利运行它不会是一个问题,但有时它似乎完全锁定。如果卷曲请求有一个很长的延迟,我宁愿中止它并继续使用脚本。也许在连续几次失败后完全终止脚本。我认为函数调用将被线性处理,但我想也许它可能同时处理shell脚本,这可能导致问题。理想情况下,我希望得到关于脚本状态的反馈,我尝试使用状态栏来实现,但由于2011版Excel的限制,这不起作用。在运行开始时,我可以看到正在更新的工作表和一切运行顺利,但稍后在脚本excel冻结直到完成。

另一个解决方案是使用MacScript命令,但是这个shell脚本包含带有“\”和多个引号的正则表达式,并且很难转换为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
vba excel-vba shell curl excel-vba-mac
1个回答
2
投票

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

VBA代码总是比Excel对象或popen等外部函数提供的任何方法慢。例如,使用“范围对象”自动填充方法将始终比使用值填充范围的循环中的VBA更快。

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

我将它放在End IfWend之间,如下所示:

        End If
    VBA.DoEvents
Wend

这将导致While循环允许Excel和Windows处理事件。除VBA Shell()函数外,VBA代码是串行执行的。如果没有VBA.DoEvents,您的代码将继续执行直到完成或错误。这个Stack Overflow question thread提供了很好的解释。

可以加快代码速度的一件事是使用此命令Application.ScreenUpdating = False关闭屏幕更新。记得用Application.ScreenUpdating = True将其重新打开。我把它放在While循环这样:

Application.ScreenUpdating = False
While feof(file) = 0 
.
.
.
Wend
Application.ScreenUpdating = True

此外,如果你真的喜欢冒险,你可以写一个事件处理程序。 Chip Pearson有一篇很棒的文章“Events and Event Procedures in VBA”

© www.soinside.com 2019 - 2024. All rights reserved.