Функция оболочки VBA в Office 2011 для Mac

#excel #vba #macos #shell

#excel #vba #macos #оболочка

Вопрос:

Я пытаюсь запустить сценарий оболочки из макроса VBA в Word 2011 для Mac, который будет выполняться в окне терминала. Я пытался использовать как функцию оболочки, так и MacScript функцию, но интерпретатор VBA, похоже, не может найти скрипт ни в том, ни в другом случае.

Согласно справочной документации по VBA, должно работать следующее:

  RetVal = Shell("Macintosh HD:Applications:Calculator.app", vbNormalFocus)
  

Это приводит к ошибке времени выполнения 53 «Файл не найден».

Есть предложения?

Комментарии:

1. Присутствует ли приложение calculator в этом расположении?

2. Последний стиль может быть таким RetValue = Shell("/System/Applications/Calculator.app", vbNormalFocus) ?

Ответ №1:

Shell() Функции VBA на Mac, по-видимому, требуется полный путь в виде пути в стиле HFS (с двоеточиями вместо косых черт). Похоже, что она также не принимает аргументы, как в Windows (при добавлении каких-либо аргументов сообщается об ошибке «Путь не найден»).

Также можно использовать MacScript() функцию VBA: MacScript("do shell script ""command""") . Вероятно, это самый простой вариант, который я бы предложил сделать. Недостатком является то, что у него довольно много накладных расходов (100-200 мс на вызов).

Другой альтернативой является system() функция из стандартной библиотеки C:

 Private Declare Function system Lib "libc.dylib" (ByVal command As String) As Long

Sub RunSafari()
    Dim result As Long
    result = system("open -a Safari --args http://www.google.com")
    Debug.Print Str(result)
End Sub
  

Смотрите http://pubs.opengroup.org/onlinepubs/009604499/functions/system.html для документации.

system() возвращает только код выхода. Если вы хотите получить выходные данные из команды, вы могли бы использовать popen() .

 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 amp; chunk
        End If
    Wend

    exitCode = pclose(file)
End Function

Sub RunTest()
    Dim result As String
    Dim exitCode As Long
    result = execShell("echo Hello World", exitCode)
    Debug.Print "Result: """ amp; result amp; """"
    Debug.Print "Exit Code: " amp; str(exitCode)
End Sub
  

Обратите внимание, что некоторые из Long аргументов в приведенном выше примере являются указателями, поэтому их придется изменить, если когда-либо будет выпущена 64-разрядная версия Mac Word.

Комментарии:

1. Спасибо! это было чрезвычайно полезно: Another alternative is the system() function from the standard C library:

2. «Обратите внимание, что несколько длинных аргументов в приведенном выше примере являются указателями, поэтому их придется изменить, если когда-либо будет выпущена 64-разрядная версия Mac Word». Это действительно произошло! Что теперь мне делать?

3. Идеально ~! Пожалуйста, давайте не забудем добавить ‘PtrSafe’ перед ‘Function’ для более поздней версии Excel, например, «Private Declare Function system Lib …».

4. Смотрите мой ответ ниже об обновлениях кода Роберта, чтобы сделать его совместимым с 64-разрядным Office, и о переходе на «песочницу».

5. Начиная с Big Sur, библиотека должна быть объявлена как «/usr/lib/libc .dylib»

Ответ №2:

Надеюсь, вы уже нашли ответ, но вам просто нужен полный путь:

 RetVal = Shell("Macintosh HD:Applications:Calculator.app:" amp; _
              "Contents:MacOS:Calculator", vbNormalFocus)
  

Другим примером является что-то вроде:

 RetVal = Shell("Macintosh HD:Users:brownj:Documents:" amp; _
              "rnaseq:IGV_2.0.14:igv.sh", vbNormalFocus)
  

Комментарии:

1. это может больше не работать в Excel 2016 для Mac из-за изолированной среды

Ответ №3:

Другая проблема выглядит точно так: разрешения приводят к сбою вашего скрипта из-за различий между средой AppleScript и средой bash вашего пользователя. Эти вопросы и ответы помогли мне разобраться в этом. Чтобы заставить мой скрипт работать, мне пришлось решить некоторые проблемы с путями и разрешениями (не с самим скриптом, а с элементами, затронутыми скриптом).

Вот моя рекомендация, которая, надеюсь, даст лучшее представление при устранении неполадок, чем бессмысленные ошибки Excel, которые я видел до использования редактора AppleScript:

  1. Используйте редактор AppleScript, чтобы убедиться, что скрипт действительно работает от имени любого пользователя и с любой используемой переменной среды:

    1. В Spotlight начните вводить «редактор applescript», пока он не появится, а затем нажмите на него
    2. Создайте новый файл редактора AppleScript
    3. Введите свой простой скрипт в новый файл, не заключая его в двойные кавычки — мой читает

       do shell script "parseCsvAndOpen.sh"
        
    4. Нажмите кнопку «Выполнить» для вашего скрипта
    5. Отследите все проблемы, внесите изменения и повторяйте нажатие кнопки «Выполнить», пока не запустите ее из редактора AppleScript
      • Хорошей новостью здесь является то, что у вас есть более узкий поиск, если вам нужно вернуться к StackOverflow или Google за помощью 😉
  2. теперь скопируйте ваш простой скрипт из редактора AppleScript в ваш vba и убедитесь, что он все еще работает

    1. Я смог просто удвоить свои двойные кавычки и поместить их в двойные кавычки после кода MacScript:

       MacScript "do shell script ""parseCsvAndOpen.sh"""
        

      Это действительно один, два, а затем и три символа двойной кавычки! (предположительно, избегает двойных кавычек)

Комментарии:

1. Мне нужно было иметь: MacScript «выполнить сценарий оболочки «»/Users/xxx/…/doit.sh «»» т.е. полный путь оболочки к сценарию

Ответ №4:

Для всех, кто использует 64-разрядный Office, вот обновленный код Роберта, совместимый как с 32, так и с 64-разрядными версиями. Обратите внимание, что из-за изменения в изолированной среде в объявлениях должен использоваться полный путь к libc, иначе вы получите сообщение об ошибке 53 «Файл не найден» в никогда не используемых версиях OSX / Office.

 #If Mac Then
  #If VBA7 Then
    ' 64 bit Office:mac
    Private Declare PtrSafe Function popen Lib "/usr/lib/libc.dylib" (ByVal command As String, ByVal mode As String) As LongPtr
    Private Declare PtrSafe Function pclose Lib "/usr/lib/libc.dylib" (ByVal file As LongPtr) As Long
    Private Declare PtrSafe Function fread Lib "/usr/lib/libc.dylib" (ByVal outStr As String, ByVal size As LongPtr, ByVal items As LongPtr, ByVal stream As LongPtr) As Long
    Private Declare PtrSafe Function feof Lib "/usr/lib/libc.dylib" (ByVal file As LongPtr) As LongPtr
    Private file As LongPtr
  #Else
    ' 32 bit Office:mac
    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
    Private file As Long
  #End If

  Public Function execShell(command As String, Optional ByRef exitCode As Long) As String
    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 amp; chunk
      End If
    Wend
    
    exitCode = pclose(file) ' 0 = success
  End Function

#End If
  

Комментарии:

1. Начиная с Big Sur, библиотека должна быть объявлена как «/usr/lib/libc .dylib»

Ответ №5:

Просто недостаточно баллов, чтобы сделать комментарий, но воспринимайте это как дополнение к ответу Робина Найтса. Конечно, заслуги по-прежнему принадлежат ему.
Для Excel 15.18 (2015) open вызову, используемому в system() функции из стандартной библиотеки C, больше не требуется --args ключевое слово:

 Private Declare Function system Lib "libc.dylib" (ByVal command As String) As Long

Sub RunSafari()
    Dim result As Long
    result = system("open -a Safari http://www.google.com")
    Debug.Print Str(result)
End Sub
  

Это работает нормально. Не тестировал это в 2011 году.