Вызов PowerShell из встроенного языка 1С: три рабочих способа
Содержание
Скрипт PowerShell из 1С запускают одной строкой: ЗапуститьПриложение("powershell.exe -NoProfile -ExecutionPolicy Bypass -File ""C:\Scripts\task.ps1""", , Истина, КодВозврата). Если нужен скрытый запуск без мелькания окна — через COM-объект WScript.Shell и его метод Run. Если нужно прочитать вывод скрипта — через WScript.Shell.Exec или через файл результата.
Все три способа работают только под Windows: на клиенте или на сервере 1С, развёрнутом на Windows. Код ниже проверен на логике платформы 8.3 и PowerShell 5.1, который встроен в Windows 10/11 и Windows Server 2016 и новее.
Что проверить до первого запуска
Половина проблем с вызовом PowerShell из 1С — не в коде 1С, а в окружении. Перед тем как писать модуль, ответьте себе на четыре вопроса.
Где выполнится код. Это главное.
- 🖥️ В клиентском модуле (
&НаКлиенте) скрипт стартует на компьютере пользователя, под его учётной записью. - 🗄️ В серверном модуле (
&НаСервере) или в регламентном задании — на сервере 1С, под учётной записью службы агента сервера (обычноUSR1CV8). - 🌐 В веб-клиенте внешние программы и COM-объекты не запускаются вовсе.
У службы USR1CV8 нет подключённых сетевых дисков, свой профиль и, как правило, урезанные права. Скрипт, который «у меня работает», на сервере часто падает именно поэтому.
Какая разрядность клиента. 32-битный клиент 1С вызывает 32-битный powershell.exe из C:\Windows\SysWOW64\WindowsPowerShell\v1.0\. У него своя политика выполнения и свой набор модулей. Модуль, установленный для 64-битного PowerShell, в 32-битном может не найтись.
Какая политика выполнения. По умолчанию на клиентских Windows стоит Restricted — скрипты из файлов не запускаются. Посмотреть все уровни политики:
Get-ExecutionPolicy -List
Нет ли блокировки файла. Скрипт, скачанный из интернета или из почты, Windows помечает как полученный извне. С политикой RemoteSigned такой файл не выполнится, пока его не разблокировать:
Unblock-File -Path C:\Scripts\task.ps1
Политика: обойти или настроить
| Вариант | Как задать | Когда подходит |
|---|---|---|
| Ключ запуска | -ExecutionPolicy Bypass в командной строке | Скрипт свой, лежит в защищённой папке |
| Политика для пользователя | Set-ExecutionPolicy RemoteSigned -Scope CurrentUser | Одна учётная запись, без прав администратора |
| Политика для машины | Set-ExecutionPolicy RemoteSigned -Scope LocalMachine от администратора | Сервер 1С, скрипты пишет ваша команда |
| Подпись скриптов | Сертификат подписи кода + политика AllSigned | Жёсткие требования безопасности |
Ключ -ExecutionPolicy Bypass — не дыра, а штатная возможность: политика выполнения защищает от случайного запуска, а не от злоумышленника. Но папку со скриптами закройте на запись для обычных пользователей. Иначе любой, кто подменит .ps1, выполнит свой код с правами службы 1С.
Групповая политика домена перекрывает и ключ, и Set-ExecutionPolicy. Если в Get-ExecutionPolicy -List заполнены строки MachinePolicy или UserPolicy, договаривайтесь с администратором домена.
Простой запуск PowerShell из 1С через ЗапуститьПриложение
Встроенный метод не требует COM и работает и на клиенте, и на сервере. Четвёртый параметр получает код возврата, если третий (ДождатьсяЗавершения) равен Истина.
&НаСервере
Процедура ВыполнитьВыгрузкуНаСервере()
ПутьКСкрипту = "C:\Scripts\export.ps1";
Команда = СтрШаблон(
"powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File ""%1""",
ПутьКСкрипту);
КодВозврата = 0;
ЗапуститьПриложение(Команда, "C:\Scripts", Истина, КодВозврата);
Если КодВозврата <> 0 Тогда
ВызватьИсключение СтрШаблон("Скрипт %1 завершился с кодом %2",
ПутьКСкрипту, Формат(КодВозврата, "ЧГ=0"));
КонецЕсли;
КонецПроцедуры
Что делают ключи:
-NoProfile— не загружать профиль пользователя. Запуск быстрее, и чужие настройки профиля не влияют на результат.-NonInteractive— если скрипт попросит ввод, он упадёт с ошибкой, а не повиснет навсегда в ожидании.-ExecutionPolicy Bypass— политика только для этого процесса.-File— путь к скрипту. С ним код выхода изexit Nчестно доходит до 1С.
💡 Кавычки внутри строки 1С удваиваются: ""%1"" превращается в "C:\Scripts\export.ps1". Без кавычек путь с пробелом (C:\Program Files\...) разобьётся на два аргумента.
На клиенте у такого вызова один минус: на секунду мелькает чёрное окно консоли. Ключ -WindowStyle Hidden его прячет, но уже после появления. Если мелькание мешает, переходите к WScript.Shell.
В асинхронном коде на клиенте вместо модального ожидания используют ЗапуститьПриложениеАсинх с Ждать (платформа с номером не ниже 8.3.18). Параметры у него те же, код возврата приходит в результате.
Запуск PowerShell через COM-объект WScript.Shell
WScript.Shell — COM-объект Windows Script Host. Он есть в любой Windows и даёт два метода: Run и Exec.
Метод Run: скрытое окно и код возврата
Shell = Новый COMОбъект("WScript.Shell");
Команда = "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File ""C:\Scripts\task.ps1""";
// 0 — окно скрыто полностью, Истина — ждать завершения
КодВозврата = Shell.Run(Команда, 0, Истина);
Второй параметр — стиль окна: 0 прячет его целиком, 1 показывает обычно. Третий — ждать ли конца работы. С Истина метод вернёт код выхода скрипта, с Ложь — сразу 0, а скрипт продолжит работу сам.
Это лучший вариант для клиента, когда пользователь не должен видеть консоль.
Метод Exec: чтение вывода скрипта
Exec возвращает объект процесса, у которого есть потоки StdOut и StdErr, свойства Status и ExitCode.
Shell = Новый COMОбъект("WScript.Shell");
Процесс = Shell.Exec("powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File ""C:\Scripts\check.ps1""");
Вывод = Процесс.StdOut.ReadAll();
ТекстОшибок = Процесс.StdErr.ReadAll();
// Status = 0 — процесс ещё работает, 1 — завершён
Пока Процесс.Status = 0 Цикл
КонецЦикла;
Если Процесс.ExitCode <> 0 Тогда
ВызватьИсключение "PowerShell вернул ошибку: " + ТекстОшибок;
КонецЕсли;
Ограничения Exec, о которых узнают на практике:
- 🪟 окно консоли нельзя скрыть — у метода нет параметра стиля окна;
- 🔤 кириллица в
StdOutприходит в кодировке консоли и нередко превращается в «кракозябры»; - 🔒 если скрипт пишет много в
StdErr, а вы читаете сначалаStdOutцеликом, процесс может зависнуть на переполненном буфере ошибок.
Поэтому Exec годится для коротких ответов латиницей: «OK», число, код статуса. Для объёмных данных используйте файл результата — он описан ниже.
Shell.Application: запуск с повышением прав
Объект Shell.Application умеет то, чего нет у WScript.Shell, — запуск с глаголом runas, то есть с запросом повышения прав через UAC.
ShellApp = Новый COMОбъект("Shell.Application");
Аргументы = "-NoProfile -ExecutionPolicy Bypass -File ""C:\Scripts\admin-task.ps1""";
// Файл, аргументы, рабочий каталог, операция, стиль окна
ShellApp.ShellExecute("powershell.exe", Аргументы, "C:\Scripts", "runas", 1);
Особенности метода:
- Он не ждёт завершения и не возвращает код выхода. Результат придётся забирать из файла или журнала.
- С
runasпользователь увидит окно UAC. Если он нажмёт «Нет», 1С получит исключение — оберните вызов вПопытка. - На сервере 1С так делать бессмысленно: служба работает без рабочего стола, подтвердить запрос некому.
Без runas (пустая строка в четвёртом параметре) ShellExecute — просто запуск «как двойной щелчок». Удобно, когда нужно открыть результат работы скрипта, например готовый отчёт, в связанной программе.
Какой способ выбрать
| Способ | Ждёт завершения | Код возврата | Читает вывод | Скрыть окно | Где работает |
|---|---|---|---|---|---|
ЗапуститьПриложение | Да | Да | Нет | Мелькает | Клиент и сервер |
WScript.Shell.Run | Да | Да | Нет | Да | Клиент и сервер на Windows |
WScript.Shell.Exec | Через Status | Да | Да | Нет | Клиент на Windows |
Shell.Application | Нет | Нет | Нет | Да | Только клиент |
Как вернуть результат скрипта в 1С
Самый надёжный канал — временный файл в UTF-8. Скрипт пишет туда JSON, 1С читает его штатным ЧтениеJSON. Кодировки консоли больше не мешают, объём данных не ограничен.
Сторона PowerShell (C:\Scripts\disk-info.ps1):
param(
[Parameter(Mandatory = $true)]
[string]$OutFile
)
$ErrorActionPreference = 'Stop'
$disks = Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" |
ForEach-Object {
[PSCustomObject]@{
Name = $_.DeviceID
FreeGb = [math]::Round($_.FreeSpace / 1GB, 1)
}
}
@{ Disks = @($disks) } | ConvertTo-Json -Depth 3 |
Set-Content -Path $OutFile -Encoding UTF8
exit 0
Сторона 1С:
ФайлРезультата = ПолучитьИмяВременногоФайла("json");
Команда = СтрШаблон(
"powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File ""%1"" -OutFile ""%2""",
"C:\Scripts\disk-info.ps1", ФайлРезультата);
КодВозврата = 0;
ЗапуститьПриложение(Команда, , Истина, КодВозврата);
Если КодВозврата = 0 Тогда
Чтение = Новый ЧтениеJSON;
Чтение.ОткрытьФайл(ФайлРезультата, КодировкаТекста.UTF8);
Данные = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
Для Каждого Диск Из Данные.Disks Цикл
Сообщить(СтрШаблон("%1 свободно %2 ГБ", Диск.Name, Диск.FreeGb));
КонецЦикла;
КонецЕсли;
УдалитьФайлы(ФайлРезультата);
💡 Обёртка @{ Disks = @($disks) } нужна, чтобы при одном диске в JSON всё равно пришёл массив. Без неё ConvertTo-Json выдаст одиночный объект, и цикл Для Каждого в 1С сломается.
Если нужен простой текст, а не JSON, читайте файл через ТекстовыйДокумент.Прочитать(ФайлРезультата, КодировкаТекста.UTF8). В PowerShell 5.1 Set-Content -Encoding UTF8 пишет файл с BOM, и 1С распознаёт его без проблем.
Передача параметров в скрипт PowerShell
Параметры объявляют блоком param() в начале скрипта и передают по имени после пути к файлу:
param(
[Parameter(Mandatory = $true)][string]$OrgInn,
[Parameter(Mandatory = $true)][datetime]$DateFrom,
[int]$Limit = 100,
[switch]$DryRun
)
Вызов из 1С:
Аргументы = СтрШаблон("-OrgInn ""%1"" -DateFrom ""%2"" -Limit %3 -DryRun",
Организация.ИНН,
Формат(НачалоПериода, "ДФ=yyyy-MM-dd"),
Формат(Лимит, "ЧГ=0"));
Команда = "powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File ""C:\Scripts\load.ps1"" " + Аргументы;
Правила, которые снимают большинство проблем:
- 📅 Даты передавайте в формате
yyyy-MM-dd. Строку вида01.10.2026PowerShell разберёт по-разному в зависимости от региональных настроек учётной записи. - 🔢 Числа форматируйте через
Формат(..., "ЧГ=0"), иначе в строку попадёт неразрывный пробел-разделитель тысяч и параметр не распознается. - 🔘 Флаг
[switch]передаётся одним именем, без значения:-DryRun. - 📝 Строку с пробелами берите в двойные кавычки, а кавычки внутри самого значения уберите или замените.
Для длинных или вложенных данных (таблица контрагентов, список файлов) командная строка не подходит: её длина ограничена, а экранирование быстро превращается в головоломку. Запишите параметры в JSON-файл через ЗаписьJSON, а в скрипт передайте только путь к нему. Внутри PowerShell: Get-Content $ParamFile -Raw -Encoding UTF8 | ConvertFrom-Json.
Пароли и токены не передавайте в командной строке. Её видно в диспетчере задач и в журнале событий, если включён аудит создания процессов. Секрет держите в защищённом хранилище учётных данных Windows или в файле с правами только для учётной записи, под которой работает скрипт.
Отладка и обработка ошибок PowerShell в 1С
Как сделать так, чтобы 1С узнала о сбое
По умолчанию многие ошибки PowerShell «непрерывающие»: скрипт пишет красный текст и идёт дальше, а в конце возвращает код 0. Для 1С это выглядит как успех.
Исправляется двумя приёмами в самом скрипте:
$ErrorActionPreference = 'Stop'
try {
# основная работа
Copy-Item -Path $Source -Destination $Target
exit 0
}
catch {
[Console]::Error.WriteLine($_.Exception.Message)
exit 1
}
Свои коды выхода (2 — нет файла, 3 — нет доступа к сети) позволяют в 1С показать пользователю понятное сообщение, а не «что-то пошло не так».
На стороне 1С сам вызов COM-объекта тоже оборачивайте в Попытка ... Исключение. При ошибке создания объекта платформа выдаёт Ошибка при вызове конструктора (COMОбъект) — значит, объект недоступен в этом контексте: веб-клиент, сервер на Linux или запрет на уровне безопасного режима.
Порядок поиска причины
- Скопируйте итоговую строку команды (выведите её через
Сообщитьили в журнал регистрации) и выполните вcmdвручную. Код выхода посмотрите командойecho %ERRORLEVEL%. - Если вручную работает, а из серверного кода нет — запустите
cmdот имени учётной записи службы 1С (черезrunas /user:...или планировщик заданий) и повторите. - Включите журнал в скрипте:
Start-Transcript -Path C:\Scripts\logs\task.log -Appendв начале иStop-Transcriptв конце. Папкаlogsдолжна быть доступна на запись учётной записи службы. - Проверьте разрядность: в скрипте выведите
[Environment]::Is64BitProcessв журнал.
Restricted. Добавьте в команду -ExecutionPolicy Bypass или задайте RemoteSigned. Если не помогло — политику задаёт групповая политика домена, смотрите Get-ExecutionPolicy -List.USR1CV8 на папки и сетевые ресурсы, замените буквы сетевых дисков на UNC-пути \\server\share.-NonInteractive, параметр -Confirm:$false у командлетов и собственный тайм-аут внутри скрипта.Безопасный режим и внешние обработки
Код внешней обработки или расширения, работающий в безопасном режиме, не может создавать COM-объекты и запускать внешние программы. Это ограничение платформы, а не ошибка в коде. Варианты: снять безопасный режим у расширения (только если вы доверяете его коду) или вынести вызов PowerShell в основную конфигурацию.
При открытии внешней обработки с таким кодом платформа может показать предупреждение защиты от опасных действий. Пользователю нужно подтвердить запуск — объясните это заранее, чтобы его не приняли за сбой.
Частые вопросы
Можно ли запустить PowerShell 7 вместо встроенного 5.1?
powershell.exe укажите pwsh.exe или полный путь к нему. Ключи -NoProfile, -ExecutionPolicy, -File те же. В PowerShell 7 Set-Content -Encoding UTF8 пишет файл без BOM, 1С читает его с КодировкаТекста.UTF8 так же.Что делать, если сервер 1С работает на Linux?
ЗапуститьПриложение выполнит команду оболочки Linux. Если на сервере установлен PowerShell 7, вызывайте pwsh -NoProfile -File /opt/scripts/task.ps1.Можно ли выполнить команду PowerShell без отдельного файла .ps1?
-Command "...". Но экранировать кавычки и спецсимволы в строке 1С неудобно, а код выхода передаётся менее предсказуемо. Для всего длиннее одной команды файл надёжнее.Как запускать скрипт по расписанию из 1С?
Как ограничить время работы скрипта?
Wait-Job -Timeout для фоновых заданий. У объекта Exec есть метод Terminate(), но ждать в цикле на стороне 1С — пустая нагрузка на процессор.