#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:
-
Используйте редактор AppleScript, чтобы убедиться, что скрипт действительно работает от имени любого пользователя и с любой используемой переменной среды:
- В Spotlight начните вводить «редактор applescript», пока он не появится, а затем нажмите на него
- Создайте новый файл редактора AppleScript
-
Введите свой простой скрипт в новый файл, не заключая его в двойные кавычки — мой читает
do shell script "parseCsvAndOpen.sh"
- Нажмите кнопку «Выполнить» для вашего скрипта
- Отследите все проблемы, внесите изменения и повторяйте нажатие кнопки «Выполнить», пока не запустите ее из редактора AppleScript
- Хорошей новостью здесь является то, что у вас есть более узкий поиск, если вам нужно вернуться к StackOverflow или Google за помощью 😉
-
теперь скопируйте ваш простой скрипт из редактора AppleScript в ваш vba и убедитесь, что он все еще работает
-
Я смог просто удвоить свои двойные кавычки и поместить их в двойные кавычки после кода 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 году.