Скрипты и скриптовый язык 1C:EDT CLI

Скрипты и скриптовый язык 1C:EDT CLI #

Cкрипт 1C:EDT CLI - это текстовый файл, содержащий инструкции скриптового языка 1C:EDT CLI (в простом случае — одну или несколько последовательных команд), который можно запустить с помощью аргумента командной строки -file и в который можно передать аргументы, доступные из скрипта как переменные $1, $2 и т.д. Обычно файлы скриптов имеют расширение .1cedtcli.

В версии 1C:EDT 2024.2 появилась расширенная поддержка скриптов:

  1. Скрипты могут быть запущены как обычные команды CLI в интерактивном режиме или режиме -command.
  2. Скрипты могут быть включены в поставку продукта.
  3. Команда help не делает различий между командами и скриптами: список выводимых команд содержит загруженные скрипты, можно получить справку по скрипту.
Таким образом стирается грань между понятиями “команда” и “скрипт”. Скрипты можно рассматривать как команды, которые проще реализовать и добавить в продукт.

Загрузка скриптов #

Поставляемые вместе с продуктом скрипты загружаются автоматически. Их список можно получить командой script.

Загрузить внешние (не входящие в дистрибутив продукта) скрипты можно несколькими способами:

  1. Перечислить в переменной окружения EDTCLI_SCRIPT_PATH пути к папкам со скриптами (формат аналогичен переменной PATH).
  2. Использовать команду script --load.

В обоих случаях из указанных папок будут загружены все файлы с расширением .1cedtcli. Именем скрипта будет имя его файла без расширения. Имя файла не должно содержать специальных символов.

Для динамической верификации скриптов можно подключиться к точке расширения com.e1c.g5.v8.dt.cli.api.cliVerifier классом, реализующим интерфейс com.e1c.g5.v8.dt.cli.api.ICliVerifier. Это позволит запрещать загрузку определенных скриптов. Верификация проводится как для поставляемых скриптов, так и для внешних.

Запуск скриптов #

Скрипты могут запускаться как обычные команды:

<имя скрипта> <аргумент1> <аргумент2> ...

Также с ними работает команда help.

Пример: создадим файл hello.1cedtcli со следующим содержимым:

# Скрипт приветствует пользователя.
#
# Использование:
#   hello "имя"
# Аргументы:
#   имя - имя пользователя. Если не задано, приветствует мир.
# 
# Дополнительно скрипт распечатывает свое расположение.

name = мир
if { $1 } {
    name = $1
}

echo Привет, $name!
echo Расположение скрипта: $0

Запустим 1C:EDT CLI в интерактивном режиме и загрузим его:

1C:EDT> script --load "C:\Users\test\cli-scripts\hello.1cedtcli"
hello

Вывод hello выше означает, что скрипт успешно загружен. Теперь мы можем получить по нему справку

1C:EDT> help hello
Скрипт приветствует пользователя.

Использование:
  hello "имя"
Аргументы:
  имя - имя пользователя. Если не задано, приветствует мир.

Дополнительно скрипт распечатывает свое расположение.

…и запустить как обычную команду в интерактивной режиме

1C:EDT> hello
Привет, мир!
Расположение скрипта: C:\Users\test\cli-scripts\hello.1cedtcli
1C:EDT> hello Иван
Привет, Иван!
Расположение скрипта: C:\Users\test\cli-scripts\hello.1cedtcli

Если мы хотим использовать этот скрипт в режиме -command, то нам нужно установить значение переменной окружения EDTCLI_SCRIPT_PATH=C:\Users\test\cli-scripts. При указании папки будут загружены все скрипты из неё. Также можно:

  • указать путь до файла - тогда будет загружен только этот скрипт;
  • указать несколько путей через стандартный разделитель (для Windows это ;, для Linux и Mac - :).

Теперь мы можем запустить скрипт в режиме -command:

$ 1cedtcli -data C:\Users\test\workspace -command hello Иван

Также при помощи команды script -c/--content можно увидеть содержимое скрипта (его код).

Любой внешний (не входящий в дистрибутив) скрипт также можно запустить с помощью аргумента командной строки -file, для этого не требуется прописывать его в переменной EDTCLI_SCRIPT_PATH:

$ 1cedtcli -data C:\Users\test\workspace -file C:\Users\test\cli-scripts\hello.1cedtcli Иван

Скриптовый язык #

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

Символ # и остаток строки после него - это комментарий.

Если скрипт начинается с комментария, то этот комментарий трактуется как справка о скрипте, и он будет показан командой help.

Литералы и переменные #

Поддерживаются следующие типы литералов:

  1. Строка: "abcd", кавычки не обязательны.
  2. Список: [1 2 a b]
  3. Словарь: [Jan=1 Feb=2 Mar=3]
  4. Замыкание: { echo hi $args } (см. ниже).

Определить переменную можно так: a = test. Получить значение переменной: $a.

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

Предопределенные переменные #

При выполнении скрипта следующие переменные определены автоматически.

  1. $0 - путь до файла исполняемого скрипта. В случае поставляемого скрипта - это путь до ресурса (см. ниже раздел “Поставка скриптов”).
  2. $argc - количество переданных аргументов.
  3. $1, $2, ... - аргументы, переданные скрипту.

Последовательное исполнение команд, перенаправление вывода #

Чтобы последовательно выполнить несколько команд в одной строке, используйте ;:

echo a; echo b

Поддерживается перенаправление вывода одной команды на ввод другой (pipes):

1C:EDT> platform-versions | grep 8.3.22
8.3.22
true

Вывод команды можно перенаправить в переменную с помощью скобок:

1C:EDT> q = (echo abcd)
abcd
1C:EDT> $q
abcd

Вывод команды можно перенаправить в файл с помощью >:

1C:EDT> platform-versions > /home/test/platform-versions.txt
1C:EDT> 

Замыкания #

Замыкание - блок кода, который может быть сохранен в переменную или передан как аргумент в команду для исполнения позднее. Оформляется фигурными скобками:

1C:EDT> hi-func = { echo Привет, $args! }
echo Привет, $args!
1C:EDT> hi-func Имярек
Привет, Имярек!

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

# правильно
hi-func = {
    echo Привет, $args!
    echo Как дела?
}

# неправильно:
hi-func =
{
    echo Привет, $args!
    echo Как дела?
}

Управление ходом исполнения #

Команды управления ходом исполнения получают на вход замыкания, к ним применяются правила оформления замыканий. Например:

# правильно:
if { условие } {
    действие
}

# неправильно:
if { условие }
{
    действие
}

Ветвление #

if { условие } { действие, если условие истинно } { действие, если условие ложно }

Последний блок (else) необязателен и может быть опущен.

В качестве условия может выступать результат выполнения команды/скрипта или значение переменной. Следующие значения считаются ложными:

  • null
  • false (булево значение)
  • 0 (число)
  • 0 (строка)
  • пустая строка
  • строка, состоящая из одних пробельных символов
  • любая пустая коллекция.

Остальные значения считаются истинными.

Инвертировать условия можно командой not { условие }.

Циклы #

while { условие } { действие, пока условие истинно }
until { условие } { действие, пока условие ложно }

Условие истинности такое же, как для ветвлений.

each $коллекция { действие }

Выполняет действие для каждого элемента коллекции. Текущий элемент доступен в переменной it. См. пример ниже.

Работа с исключениями #

try { основное действие } { действие в случае исключения } { завершающее действие }

# аналогично первому варианту, но более читабельно:
try { основное действие } catch { действие в случае исключения } finally { завершающее действие }

Аналог try/catch/finally в Java. Выполняется основное действие, если выпало исключение - то действие в случае исключения. Завершающее действие выполняется всегда.

Третий блок (finally) не обязателен и может быть опущен.

Если нужно сделать try/finally (без catch), используйте следующую форму:

try { основное действие } finally { завершающее действие }

Бросить исключение:

throw строка/число/любой объект

Если выброшено исключение, оно будет доступно в переменной exception. В переменной будет хранится объект исключения, у него можно вызвать методы, например, message или printStackTrace:

1C:EDT> throw "Произошла ошибка"
edtsh: Произошла ошибка
Введите '$exception printStackTrace' для получения детальной информации об ошибке.
1C:EDT> $exception message
Произошла ошибка

Успешное выполненные команды обнуляют переменную exception. Если информация об исключении нужна позднее, сохраните его в другую переменную: handle_later = $exception.

Служебные команды #

echo: вывод #

1C:EDT> echo Всем привет!
Всем привет!

grep: поиск #

1C:EDT> platform-versions | grep -q 8.3.24
true

Необязательные аргументы:

  • -i, --ignore-case: игнорировать регистр символов
  • -n, --line-number: добавлять к каждой строке номер строки
  • -q, --quiet, --silent: подавить весь нормальный вывод
  • -v, --invert-match: выбирать несовпадающие строки (инверсия)

cat: чтение файлов #

cat /home/test/hello.txt

Выводит содержимое файла на консоль.

cd: текущая (рабочая) директория #

cd
cd /path/to/new/working/directory

Первый вариант выводит текущую рабочую директорию (относительно которой определяются все относительные пути). Второй вариант меняет текущую рабочую директорию на указанную.

env: переменные окружения #

env
env PATH

Первый вариант выводит список всех переменных окружения. Второй вариант выводит значение указанной переменной окружения.

Взаимодействие с Java #

Все переменные являются объектами Java определенных типов, и у них можно вызывать методы и считывать свойства. Синтаксис следующий:

$<имя переменной> <имя метода или свойства> [аргументы]

Например:

1C:EDT> a = [1 2 abcd]
1
2
abcd

1C:EDT> $a size
3
1C:EDT> $a get 2
abcd
1C:EDT> 

В этом примере мы создали список из 3 элементов. Переменная a после этого имеет тип java.util.List<Object>. Далее мы вызываем метод size() у этого объекта и получаем размер списка. После чего вызываем метод get(2), чтобы получить последний элемент.

Если у типа есть геттер (метод вида getSomething()), то его можно вызвать как $a something (без get). Пример (см. пункт работы с исключениями):

$exception message

Здесь вызывается метод getMessage() у объекта исключения.

Основные команды и скрипты #

Список основных команд и скриптов зависит от комплектации дистрибутива и постоянно растет. Получить актуальный список можно, набрав команду help (без параметров).

1C:EDT> help
Доступные команды 1С:EDT CLI:

build
cd
clean-up-source
delete
exit
export
format-modules
help
import
install-platform-support
platform-versions
project
script
uninstall-platform-support
validate
lt:convert-language-project
lt:convert-languages
lt:export-translation-statistic
lt:generate-strings
lt:import-language-projects
lt:list-storages
lt:list-translation-providers
lt:synchronize

Чтобы получить справку по конкретной команде, введите ее имя после "help". Например: "help install-platform-support".

В этом списке нет служебных команд и команд управления ходом исполнения.

Поставка скриптов #

Чтобы включить скрипт в поставку продукта, нужно добавить файл скрипта в плагин и подключиться к точке расширения com.e1c.g5.v8.dt.cli.api.cliScript, указав относительный путь до файла скрипта в атрибуте resource элемента cliScript:

1
2
3
<extension point="com.e1c.g5.v8.dt.cli.api.cliScript">
    <cliScript resource="script/hello.1cedtcli"></cliScript>
</extension>

Как и для команд, для скриптов можно указать пространство имён:

1
2
3
4
<extension point="com.e1c.g5.v8.dt.cli.api.cliScript">
    <namespace name="mynamespace"></namespace>
    <cliScript resource="script/hello.1cedtcli"></cliScript>
</extension>

Тестирование скриптов #

Автоматическое тестирование поставляемых скриптов может быть реализовано через сервис com.e1c.g5.v8.dt.cli.api.ICliCommandExecutor, аналогично тому, как реализуются интеграционные тесты для команд. Подробнее см. здесь.

Примеры скриптов #

Пример 1 #

Пример выводит сообщение о том, поддерживается ли версия «1С:Предприятия» 8.3.22 данной средой разработки.

is22 = ( platform-versions | grep -q 8.3.22 )
if { $is22 } { echo 8.3.22 поддерживается } { echo 8.3.22 не поддерживается }

Вывод:

8.3.22 поддерживается

В первой строке создается переменная is22, в которую записывается результат выполнения команды в скобках. В скобках команда platform-versions выводит все поддерживаемые версии платформы, а ее вывод перенаправляется на вход команды grep, которая ищет в нем строку 8.3.22. Опция -q означает, что команда grep ничего не выводит, а просто возвращает true или false.

Во второй строке команда if проверяет истинность значения переменной is22, а команды echo выводят соответствующие сообщения.

Пример 2 #

Пример выводит сообщение о том, поддерживается ли данной средой разработки каждая из перечисленных версий «1С:Предприятия».

isList = [8.3.21 8.3.22 9.3.23]
each $isList {
    if { not { platform-versions | grep -q $it } } {
        "$it не поддерживается"
    } {
        "$it поддерживается"
    }
}

Вывод:

8.3.21 поддерживается
8.3.22 поддерживается
9.3.23 не поддерживается

В первой строке создается переменная-список интересующих версий. Во второй строке по каждой из этих версий команда if проверяет, поддерживается эта версия, или нет, и выводит соответствующее сообщение.

Пример 3 #

Пример читает содержимое файла.

hellotxt = ( cat /home/test/hello.txt )

В первой строке команда cat читает в переменную hellotxt содержимое файла /home/test/hello.txt.

Просмотр существующих скриптов #

Воспользуйтесь командой script, чтобы получить список загруженных скриптов. Далее, вызовите команду script -c <script>, чтобы изучить содержимое скрипта (его код):

script -c hello