Вызов PowerShell из встроенного языка 1С: три рабочих способа

· 11 мин

Содержание

Скрипт 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"));
    КонецЕсли;

КонецПроцедуры

Что делают ключи:

  1. -NoProfile — не загружать профиль пользователя. Запуск быстрее, и чужие настройки профиля не влияют на результат.
  2. -NonInteractive — если скрипт попросит ввод, он упадёт с ошибкой, а не повиснет навсегда в ожидании.
  3. -ExecutionPolicy Bypass — политика только для этого процесса.
  4. -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);

Особенности метода:

  1. Он не ждёт завершения и не возвращает код выхода. Результат придётся забирать из файла или журнала.
  2. С runas пользователь увидит окно UAC. Если он нажмёт «Нет», 1С получит исключение — оберните вызов в Попытка.
  3. На сервере 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.2026 PowerShell разберёт по-разному в зависимости от региональных настроек учётной записи.
  • 🔢 Числа форматируйте через Формат(..., "ЧГ=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 или запрет на уровне безопасного режима.

Порядок поиска причины

  1. Скопируйте итоговую строку команды (выведите её через Сообщить или в журнал регистрации) и выполните в cmd вручную. Код выхода посмотрите командой echo %ERRORLEVEL%.
  2. Если вручную работает, а из серверного кода нет — запустите cmd от имени учётной записи службы 1С (через runas /user:... или планировщик заданий) и повторите.
  3. Включите журнал в скрипте: Start-Transcript -Path C:\Scripts\logs\task.log -Append в начале и Stop-Transcript в конце. Папка logs должна быть доступна на запись учётной записи службы.
  4. Проверьте разрядность: в скрипте выведите [Environment]::Is64BitProcess в журнал.
Диагностика: выберите симптом

Безопасный режим и внешние обработки

Код внешней обработки или расширения, работающий в безопасном режиме, не может создавать COM-объекты и запускать внешние программы. Это ограничение платформы, а не ошибка в коде. Варианты: снять безопасный режим у расширения (только если вы доверяете его коду) или вынести вызов PowerShell в основную конфигурацию.

При открытии внешней обработки с таким кодом платформа может показать предупреждение защиты от опасных действий. Пользователю нужно подтвердить запуск — объясните это заранее, чтобы его не приняли за сбой.

Частые вопросы

Можно ли запустить PowerShell 7 вместо встроенного 5.1?
Да. Вместо powershell.exe укажите pwsh.exe или полный путь к нему. Ключи -NoProfile, -ExecutionPolicy, -File те же. В PowerShell 7 Set-Content -Encoding UTF8 пишет файл без BOM, 1С читает его с КодировкаТекста.UTF8 так же.
Что делать, если сервер 1С работает на Linux?
COM-объекты там недоступны. ЗапуститьПриложение выполнит команду оболочки Linux. Если на сервере установлен PowerShell 7, вызывайте pwsh -NoProfile -File /opt/scripts/task.ps1.
Можно ли выполнить команду PowerShell без отдельного файла .ps1?
Да, ключом -Command "...". Но экранировать кавычки и спецсимволы в строке 1С неудобно, а код выхода передаётся менее предсказуемо. Для всего длиннее одной команды файл надёжнее.
Как запускать скрипт по расписанию из 1С?
Вызов PowerShell помещают в процедуру регламентного задания. Оно выполняется на сервере, поэтому скрипт получит права службы 1С — проверьте их заранее.
Как ограничить время работы скрипта?
Надёжнее всего внутри самого скрипта: тайм-ауты у сетевых командлетов, Wait-Job -Timeout для фоновых заданий. У объекта Exec есть метод Terminate(), но ждать в цикле на стороне 1С — пустая нагрузка на процессор.