Свои функции шаблонов в оверлее
Обновлено 5 ноября 2026
Помимо встроенных функций (mergepkg(), pkg(), exists(), ini(), grep(), cut(), …) утилиты позволяют описать собственные — обычным скриптом в оверлее. Это способ вынести в шаблон проверку, которую нельзя выразить условием: есть ли сетевой интерфейс, доступен ли провайдер, какой активен профиль.
Где размещать
Функция — файл в каталоге scripts/functions/ репозитория-оверлея:
<location>/scripts/functions/checkdev.sh
Здесь <location> — путь репозитория из параметра location секции /etc/portage/repos.conf; шаблоны этого репозитория лежат в таком же <location>/profiles/templates, поэтому функция доступна во всех шаблонах репозитория. Имя функции — часть имени файла до первой точки, поэтому расширение на имя не влияет: checkdev.sh вызывается как checkdev(), а cflang.py — как cflang(). Вызывается как #-имя(аргументы)-# или, в левой части условия заголовка, просто имя(аргументы):
# Calculate checkdev(wan0)!= path=/etc/conf.d
Каким языком писать файл
Файл функции может быть написан на любом языке, интерпретатор которого есть в системе: первую строку читает сама система, а движку исполнять файл не нужно — он читает его текст и передаёт интерпретатору на вход, поэтому право на исполнение файлу не требуется. Строка #! тоже необязательна: если её нет, файл выполняется через sh. Например, файл cflang.py может выглядеть так:
#!/usr/bin/env python3
import sys
print("PY-OK " + (sys.argv[1] if len(sys.argv) > 1 else "?"))
Интерпретатор надёжнее задавать через env — #!/usr/bin/env python3, а не абсолютным путём: если указанного по абсолютному пути файла нет, наложение не просто ругается, а останавливается и не завершается; при форме с env отсутствующий интерпретатор виден по тому же предупреждению, что и остальные неполадки.
Такой файл вызывается конструкцией #-cflang(hello)-# и подставляет в шаблон PY-OK hello. Аргументы доходят до скрипта отдельными параметрами после --, так что sys.argv видит и имя функции, и её значения. Полезное следствие: текст функции сначала проходит обычную обработку шаблона, поэтому переменные и вызовы вида #-…-#, написанные внутри файла функции, подставятся ещё до запуска скрипта.
Перекрытие встроенных функций
Функция из scripts/functions/ проверяется раньше встроенной и заменяет её: если положить файл с именем
стандартной функции, метка вызовет ваш скрипт. Скрипт exists с echo SHADOW-OK возвращает в шаблоне
SHADOW-OK вместо штатного ответа встроенной функции, а скрипт на Python — PY-OK hello. Относитесь к
именам встроенных функций осторожно: подмена действует для
всех шаблонов этого репозитория, и её легко не заметить.
Протокол вызова
Функция — обычный скрипт, и договорённость про возврат значений такая:
- stdout — подставляемое значение текста;
- файловый дескриптор 3 — предупреждение (попадёт в вывод как WARNING);
- файловый дескриптор 4 — сообщение об успехе (SUCCESS);
- ненулевой код возврата — ошибка.
Пример из реального оверлея — проверка существования интерфейса:
scripts/functions/checkdev.sh
#!/bin/bash # Проверка наличия сетевого интерфейса # параметры: имя # Возвращает это же имя если интерфейс есть if ip link show $1 &>/dev/null; then echo $1 exit 0 fi
Такая функция в шаблоне даёт имя интерфейса, если он есть, и пустоту, если нет, — а вместе с условием checkdev(wan0)!= полностью отключает целый шаблон на машинах без этого интерфейса.
Два разных исхода: функция не найдена и функция ошиблась
Это важно различать при отладке.
- Имя функции не найдено (нет ни среди встроенных, ни в scripts/functions ни одного репозитория) — шаблон прерывается с сообщением
function of templates '<имя>' not found. Опечатка в имени функции стоит дороже, чем кажется: не сработает весь шаблон. - Скрипт существует, но завершился с ошибкой (ненулевой код возврата, отсутствующий интерпретатор, текст, который интерпретатор не может выполнить) — утилиты выдают предупреждение
Failed to apply custom function …и подставляют пустое значение; шаблон продолжается. Если функция стояла в условии, результат обычно будет «условие ложно», и вы получите тихо неприменённый шаблон, а не ошибку.
Практический вывод: делайте функцию «безопасной по умолчанию» — пусть при любой проблеме выводит то, что соответствует желаемому поведению при неизвестности (обычно пустую строку и exit 0), а отличить «нет данных» от «упало» вы сможете по предупреждению.
Имена, которые функциями не являются
Часть конструкций #-…-# — это переменные, а не функции, и проверять их наличие в scripts/functions бессмысленно. Например, #-cl_pass_file-# — имя обрабатываемого файла шаблона, которое утилиты подставляют сами (для директории — имя директории); именно поэтому полезен оборот #-cut(1)-#: он берёт cl_pass_file по умолчанию и делит его по -, превращая имя шаблона runlevel-<сервис> в <сервис> (нумерация с нуля: поле 1 — второе).
Различение простое: функция отличается от переменной двумя скобками передачи аргументов (), даже если аргументов нет. cl_pass_file в #-cl_pass_file-# — переменная; #-cl_pass_file()-# — уже вызов функции с этим именем. Конструкция без скобок функцией не считается никогда.
Свой скрипт важнее встроенной функции
Каталог scripts/functions/ просматривается раньше, чем список встроенных функций: если в нём есть исполняемый файл с именем, совпадающим со встроенной функцией, вызов уйдёт в ваш скрипт, а не в код утилит. Практическое следствие: скрипт вроде ini.sh или cut.sh затенит встроенные ini() и cut() во всех шаблонах этого репозитория — и вместо «переопределил функцию» вы получите тихо изменившееся поведение там, где этих имён не ждали. Так что имя встроенной функции для своего скрипта — плохое имя; при подозрительном поведении проверьте, нет ли в scripts/functions/ файла с таким же названием.
Если скрипт с одним именем есть в нескольких репозиториях, исполняется последний по списку репозиториев: сначала перечисляются Calculate-репозитории из основного repos.conf (в порядке их секций), затем прочие не-Portage репозитории из repos.conf.d/*.conf.
Приём, который стоит перенять
Функции хорошо сочетаются с cut() и ini(): имя шаблона несёт данные (link.<файл>, runlevel-<сервис>, <индекс>-<имя>), а функция их распаковывает. Так из одного короткого шаблона получается семейство ссылок и сервисных записей, а список «что настраивать» хранится в ini.env — то есть редактируется администратором, а не правкой оверлея.
См. также
- Синтаксис шаблона — встроенные функции и условия.
- Шаблон молча не сработал — различие «не найдена» / «ошиблась».
- Директории-шаблоны и слои хранения — где живут репозитории.