Каков эквивалент команды bash export -f в zsh?

Вопрос или проблема

Итак, я начал использовать zsh. Мне это нравится. Он выглядит очень круто и стильно, и тот факт, что текущий рабочий каталог и фактическая командная строка находятся на разных строках, приятен, но в то же время я замечаю, что zsh может быть немного медленнее, чем bash, особенно при выводе текста на экран.

Больше всего мне понравилось, что zsh был «обратнос совместим» со всеми функциями, которые я определил в своем .bashrc.

Однако есть одно замечание. Все функции работают идеально, но я не могу понять, как работает система экспорта.

Некоторые из тех функций .bashrc я экспортировал, чтобы использовать их в других местах, таких как скрипты и внешние программы, с помощью export -f.

В zsh о экспорте, похоже, даже не говорят. Это автозагрузка? Эти две вещи одно и то же? Я серьезно затрудняюсь это выяснить.

Переменные окружения, содержащие функции, — это хак bash. У zsh нет ничего подобного. Вы можете сделать что-то похожее с помощью нескольких строк кода. Переменные окружения содержат строки; более старые версии bash, прежде чем была обнаружена Shellshock, хранили код функции в переменной, имя которой совпадало с именем функции, а значение — () { с последующим кодом функции, а затем }. Вы можете использовать следующий код, чтобы импортировать переменные с этой кодировкой и попытаться запустить их с настройками, подобными bash. Обратите внимание, что zsh не может эмулировать все возможности bash, все, что вы можете сделать, это приблизиться (например, сделать так, чтобы $foo разделял значение и разворачивать подстановки, и сделать массивы с нуля).

bash_function_preamble="
    emulate -LR ksh
"
for name in ${(k)parameters}; do
  [[ "-$parameters[name]-" = *-export-* ]] || continue
  [[ ${(P)name} = '() {'*'}' ]] || continue
  ((! $+builtins[$name])) || continue
  functions[$name]=$bash_function_preamble${${${(P)name}#"() {"}%"}"}
done

(Как заметил Стефан Шазелас, первоначальный открыватель Shellshock, более ранняя версия этого ответа могла выполнять произвольный код в этот момент, если определение функции было повреждено. Эта версия не делает этого, но, конечно, как только вы выполните любую команду, это может быть функцией, импортированной из окружения.)

Версии bash после Shellshock кодируют функции в окружении, используя недопустимые имена переменных (например, BASH_FUNC_myfunc%%). Это делает их трудными для надежного разбора, поскольку zsh не предоставляет интерфейса для извлечения таких имен переменных из окружения.

Я не рекомендую это делать. Полагаться на экспортированные функции в скриптах — плохая идея: это создает невидимую зависимость в вашем скрипте. Если вы когда-либо выполните свой скрипт в окружении, где нет вашей функции (на другом компьютере, в задании cron, после изменения файлов инициализации вашей оболочки и т. д.), ваш скрипт больше не будет работать. Вместо этого храните все ваши функции в одном или нескольких отдельных файлах (что-то подобное ~/lib/shell/foo.sh) и запускайте свои скрипты, импортируя функции, которые они используют (. ~/lib/shell/foo.sh). Таким образом, если вы измените foo.sh, вы сможете легко искать, какие скрипты на него опираются. Если вы скопируете скрипт, вы сможете легко выяснить, какие вспомогательные файлы ему нужны.

Zsh (и ksh до него) делает это более удобным, предоставляя способ автоматически загружать функции в скрипты, где они используются. Ограничение в том, что вы можете помещать только одну функцию на файл. Объявите функцию как автозагружаемую и поместите определение функции в файл, имя которого совпадает с именем функции. Поместите этот файл в каталог, указанный в $fpath (который вы можете настроить через переменную окружения FPATH). В вашем скрипте объявите автозагружаемые функции с помощью autoload -U foo.

Кроме того, zsh может компилировать скрипты, чтобы сэкономить время разбора. Вызовите zcompile для компиляции скрипта. Это создаст файл с расширением .zwc. Если этот файл присутствует, то autoload загрузит скомпилированный файл вместо исходного кода. Вы можете использовать zrecompile для (ре)компиляции всех определений функций в каталоге.

Если вы поместите ваше объявление функции в .zshenv, ваша функция будет доступна из скрипта без каких-либо усилий.

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

Я назвал общий файл .zshared.

# ~/.zshared
function say-hello() {
  echo "Hello, $1\!"
}

Я загрузил его в своем .zshrc.

# ~/.zshrc
source ~/.zshared

В любых скриптах, которым нужен доступ к этим функциям, я просто загружаю .zshared.

#!/usr/bin/env zsh
source ~/.zshared

say-hello "David" # Hello, David!

Сначала это требует немного работы, чтобы обновить ваши скрипты, но вы все равно можете определить свои функции в одном месте без особых затрат.

Вот ваш ответ:

function hello() {
  echo "Hello ${@}"
}

export HELLO=$(functions hello)

echo 1 2 3 | xargs zsh -c 'eval $HELLO && hello ${@}' _  
Hello 1 2 3

Ответ или решение

Вопрос о том, как в zsh эквивалентно использовать команду export -f из bash, поднимает важные аспекты управления функциями и их доступностью в различных сценариях. Обсуждая этот вопрос, полезно осветить различия между bash и zsh, а также предложить подходы, работающие в zsh.

Суть проблемы

В bash команда export -f позволяет экспортировать функции, сделав их доступными для дочерних процессов. Это удобно в тех случаях, когда вы хотите использовать определённые функции в других скриптах или простых вызовах команд, создавая таким образом более модульный и управляемый код.

Однако в zsh такой функциональности нет. zsh больше ориентирован на использование функций непосредственно в контексте интерактивного окружения и не поддерживает экспорт функций через окружение, как это делает bash.

Альтернативные подходы в zsh

Автозагрузка функций

Одним из рекомендуемых методов работы с функциями в zsh является использование механизма автозагрузки. Этот метод позволяет вам организовать функции таким образом, чтобы они подгружались из файлов, что несколько аналогично принципу работы с экспортированными функциями в bash.

  1. Создание файла с функцией: Каждую функцию можно разместить в отдельном файле, имя которого совпадает с именем функции. Этот файл должен находиться в директории, которая включена в $fpath.

    # ~/functions/say-hello.zsh
    function say-hello() {
      echo "Hello, $1!"
    }
  2. Обозначение функции как автозагружаемой: В вашем сценарии или конфигурации вы можете вызвать autoload, чтобы подготовить функцию к подгрузке.

    autoload -U say-hello

Теперь функция say-hello будет загружена при первом вызове, и вы сможете использовать её подобно экспортированной функции в bash.

Общий скрипт для функций

Как показано в одном из ответов, вы также можете создать общий файл, где будут размещены ваши функции, и подключать его в нужных скриптах или в вашем файле ~/.zshrc, чтобы сделать функции доступными глобально:

# ~/.zshared
function say-hello() {
  echo "Hello, $1!"
}

В вашем ~/.zshrc:

source ~/.zshared

Теперь вы сможете использовать функции, найденные в .zshared, в любых запущенных shell-сессиях или скриптах, просто подгрузив файл.

Заключение

Хотя zsh не поддерживает export -f так, как это делает bash, существуют эффективные альтернативы, такие как автозагрузка функций и использование отдельных файлов для размещения ваших функций. Этот подход не только улучшает читаемость и структурированность кода, но и способствует лучшему управлению зависимостями в ваших скриптах. Используя предложенные методы, вы сможете эффективно адаптировать свои функции для использования вне контекста текущей оболочки.

Оцените материал
Добавить комментарий

Капча загружается...