Возможна ли автозагрузка функции lazier (k sh) в bash? - PullRequest
0 голосов
/ 07 марта 2020

Мой проект (порт из k sh) использует некоторые каталоги в качестве функций автозагрузки. В этих каталогах каждое имя файла представляет собой имя функции, объявленной внутри файла, в которой этот файл создается для объявления (реализации) функции. Каждый каталог может рассматриваться как «пакет», который дополняет встроенный набор bash с помощью функций. У меня около 20 пакетов, и количество функций в каждом пакете может быть значительным (в некоторых пакетах может достигать 30).


Документация bash включает пример реализации автозагрузки:

https://www.apt-browse.org/browse/ubuntu/trusty/main/all/bash-doc/4.3-6ubuntu1/file/usr/share/doc/bash/examples/functions/autoload.v2

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

Является реализацией это ограничение не возможно?

Ответы [ 2 ]

0 голосов
/ 10 марта 2020

Это вторая реализация, чтобы избежать использования сигнала, замеченного в предыдущей реализации, и использования command_not_found_handle (), которое кажется не полностью стабильным.

autoload::
function autoload
{ local d="$1" && [ "$1" ] && shift && autoload "$@"  
  local identifier='^[_a-zA-Z][_a-zA-Z0-9]*$'
  [ -d "$d"  -a -x "$d" ] && cd "$d" &&
  { for f in *
    do [[ $f =~ $identifier ]] && alias $f=". $PWD/$f;unalias $f;$f"
    done
    cd ->/dev/null 2>&1
  }
}


autoload $@  $(IFS=:; echo $FPATH)

Здесь мы снова получили исходный файл autolaod либо в файле r c, либо в сценариях.

Использование FPATH на самом деле не требуется (более подробную информацию о FPATH см. в Примечаниях)

Таким образом, в основном идея заключается в том, чтобы получить файл автозагрузки вместе с набором каталогов для поиска.

PW$ . /path/to/autoload a1 a2
PW$ alias | grep 'a[12c]_*'
alias a1_f1='. /home/phi/a1/a1_f1;unalias a1_f1;a1_f1'
alias a1_f2='. /home/phi/a1/a1_f2;unalias a1_f2;a1_f2'
alias a2_f1='. /home/phi/a2/a2_f1;unalias a2_f1;a2_f1'
alias ac_f3='. /home/phi/a1/ac_f3;unalias ac_f3;ac_f3'

PW$ declare -F |  grep 'a[12c]_*'

После поиска автозагрузки мы получили весь определенный псевдоним и никаких функций.

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

PW$ a1_f1 11a 11b 11c
In a1_f1() : args=11a 11b 11c

PW$ a2_f1 21a 21b 21c
In a2_f1() : args=21a 21b 21c

PW$ alias | grep 'a[12c]_*'
alias a1_f2='. /home/phi/a1/a1_f2;unalias a1_f2;a1_f2'
alias ac_f3='. /home/phi/a1/ac_f3;unalias ac_f3;ac_f3'
PW$ declare -F |  grep 'a[12c]_*'

declare -f a1_f1
declare -f a2_f1

Здесь мы видим, что a1_f1 () и a2_f2 () затем загружаются и выполняются, они удаляются из список псевдонимов и добавлен в список функций.

PW$ a1_f2 12a 12b 12c
a1_f2: command not found

PW$ ac_f3 c3a c3b c3c
In a1 ac_f3() : args=c3a c3b c3c

PW$ qqq 

Command 'qqq' not found, did you mean:

  command 'qrq' from snap qrq (0.3.1)
  command 'qrq' from deb qrq

See 'snap info <snapname>' for additional versions.

Здесь мы видим, что a1_f2 () не найден, о нем плохо сообщили, как в предыдущей реализации entation.

ac_f3 () - это та, что от a1 /, как и ожидалось.

qqq по-прежнему предоставляет результат пакета не найденной команды, если он установлен (обычно мы не связывались с command_not_found_handle ( ))

PRO и CON

PRO Не сидит на ошибке bash, то есть может жить некоторое время после bash обновлений .

CONs Немного тяжелее, чем предыдущая реализация, но приемлемо.

Намного проще, но может быть не проще, но наверняка короче, чем предложенные примеры в bash документация и немного более ленивый, то есть функция загружается только тогда, когда это необходимо (но не псевдонимы)

Файлы мультифункциональных «пакетов» вместе с жесткой ссылкой для внешнего воздействия API менее эффективны, потому что каждая функция внешнего API ( hardlink) инициирует перезагрузку файла, если файл пакета не написан хорошо, удаляя все лишние псевдонимы после загрузки.

0 голосов
/ 10 марта 2020

Ну, после того, как я спросил, моя очередь «дать» :) Сообществу. Я исследовал эту функцию автоматической загрузки, которая мне нужна, и предложил 2 реализации, я привел одну из них здесь, поэтому некоторые могут предложить улучшения или указать на ошибки. Я опубликую второй на втором посте.

В двух реализациях будет выполнено несколько тестовых случаев, поэтому перед представлением реализаций я представлю общие тестовые примеры. У нас есть 2 каталога a1 / и a2 /, в которых определения функций хоста находятся в файле с тем же именем, что и у функции, каждый каталог может считаться каталогом «package», содержащим функции для этого пакета, а затем функция там находится в пространстве имен с именем пакета. (имя dir), за редким исключением для целей тестирования.

./a1/ac_f3::
function ac_f3
{ echo "In a1 ac_f3() : args=$@"
}

./a1/a1_f1::
function a1_f1
{ echo "In a1_f1() : args=$@"
}


./a1/a1_f2::
function a1_f22
{ echo "In a1_f2() : args=$@"
}


./a2/ac_f3::
function f3
{ echo "In a2 ac_f3() : args=$@"
}

./a2/a2_f1::
function a2_f1
{ echo "In a2_f1() : args=$@"
}

ac_f3 - это функция, которая не является пространством имен, а затем является общей для обоих dir a1 / a2 /, но с другой реализацией, это должно продемонстрировать приоритет $ FPATH.

a1_f2 является поддельным, он не реализует функцию a1_f2 (), и тогда мы должны изящно потерпеть неудачу.

a1_f1, a2_f1, просто реализовать a1_f1 () a2_f2 ( ), и должен быть найден и выполнен.

реализация command_not_found_handle

Спасибо Чарльзу за использование опции command_not_found_handle, потому что, безусловно, автоматически загружаемая функция связана с тем фактом, что 'команда' не найдена, и затем мы пытаемся найти автоматическую загрузку для загрузки и выполнения.

Но, что удивительно, оболочка bash имеет интересную «особенность», то есть некоторое недокументированное поведение.

Bash do c говорит.

   If the search is unsuccessful, the shell searches for a defined
   shell function named command_not_found_handle.  If that
   function exists, it is invoked with the original command and
   the original command's arguments as its arguments, and the
   function's exit status becomes the exit status of the shell.
   If that function is not defined, the shell prints an error
   message and returns an exit status of 127.

Это вводит в заблуждение, потому что здесь мы говорим о вызове функции command_not_found_handle (), а затем можем вывести «из контекста оболочки», а это не так.

В логах оболочки c нам не удалось получить псевдоним, затем не удалось получить функцию, затем не удалось получить программу «external to the shell», и оболочка уже находится в подпрограмме. режим создания оболочки, так что command_not_found_handle () вызывается, но в подоболочке. не контекст оболочки. Это может быть хорошо, но «забавная особенность» здесь в том, что созданный подпроцесс не является чистым, его $$ и $ PPID установлены неправильно, возможно, это будет исправлено однажды. Чтобы продемонстрировать эту функцию bash, мы можем сделать

function command_not_found_handle
{ echo $$ ; sh -c 'echo $PPID'
}

PW$ # In a shell context invocation    
PW$  command_not_found_handle
2746
2746

PW$ # In a subshell invocation (via command not found)    
PW$ qqq
2746
3090

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

# autoload
# This file must be sourced
# - From your rc files if you need autoloadable fuctions from your
#   interactive shell
# - From any script that need autoloadable functions.
#
# The FPATH must be set with a set of dirs/ where to look to find
# file name match the function name to source and execute.
#
# Note that if FPATH is exported, this is a way to export functions to
# script subshells

# Create a default command_not_found_handle if none exist
declare -F command_not_found_handle >/dev/null ||
function command_not_found_handle { ! echo bash: $1 command not found>&2; }

# Rename current command_not_found_handle
_cnf_body=$(declare -f command_not_found_handle | tail -n +2)
eval "function _cnf_prev $_cnf_body"

# Change USR1 to your liking
CNF_SIG=USR1

function autoload
{ declare f=$1 ; shift
  declare d s
  for d in $(IFS=:; echo $FPATH)
  do s=$d/$f
    [ -f $s -a -r $s ] &&
    { . $s
      declare -F $f >/dev/null ||
      { echo "$s exist but don't define $f" >&2 ; return 127
      }
      $f "$@" ; return
    }
  done
  _cnf_prev $f "$@"
}

trap 'autoload ${BASH_COMMAND[@]}' $CNF_SIG
function command_not_found_handle
{ kill -$CNF_SIG $$
}

ПРЕДУПРЕЖДЕНИЕ, если вы когда-либо используете этот файл автозагрузки подготовлен для исправления bash, он может однажды отразить реальный $$ $ PPID, и в этом случае вам нужно будет исправить приведенный выше фрагмент с $ PPID вместо $$ .

Результаты.

PW$ . /path/to/autoload
PW$ FPATH=a1:a2

PW$ a1_f1 11a 11b 11c
In a1_f1() : args=11a 11b 11c

PW$ a2_f1 21a 21b 21c
In a2_f1() : args=21a 21b 21c

PW$ a1_f2 12a 12b 12c
a1/a1_f2 exist but don't define a1_f2

PW$ ac_f3 c3a c3b c3c
In a1 ac_f3() : args=c3a c3b c3c

PW$ qqq  
Command 'qqq' not found, did you mean:

  command 'qrq' from snap qrq (0.3.1)
  command 'qrq' from deb qrq

See 'snap info <snapname>' for additional versions.

То, что мы получили здесь, правильно, a1_f1 () a2_f1 () найдены, загружены, выполнены.

a1_f2 () нигде не найти, несмотря на наличие файла, в котором он может быть размещен.

В вызове qqq отображаются цепочки обработчиков, идет функция автозагрузки fist, затем пакет ubuntu command-not-found (если установлен) ) значит мы не теряйте пользовательский интерфейс command_not_found_handle ().

Обратите внимание, что здесь нет функций 'admin', таких как добавление / удаление / перезагрузка функций.

Добавление - это вопрос установки файла в каталогах, присутствующих в $ FPATH

Удаление - это удаление исходного файла и отключение -f функции

Перезагрузка - это вопрос редактирования исходного файла и отмены -f функции.

Функция перезагрузки может быть довольно удобной во время разработки в интерактивной оболочке, но все это можно сделать с помощью простого unset -f funcname, так что в основном вы редактируете свой исходный файл , сбросьте функцию, затем вызовите ее, вы получите последнюю версию. То же самое может произойти в демоне сценария, можно реализовать сигнал для демона, и обработчик ловушек просто сбросит набор функций, которые затем будут перезагружены без остановки / перезапуска демона.

Еще одна особенность заключается в том, что возможна оболочка «пакет», то есть исходный файл может реализовывать «много» функций, некоторые являются внешним API, другие являются внутренними по отношению к пакету, поскольку в оболочке все плоско, функции располагаются в пространстве имен, а затем каждый из внешних функций API ( хотя и задокументировано) может быть жестко связано с тем же файлом. Первый использованный внешний API загрузит все функции пакета.

В моем проекте документация извлекается из источников пакетов, а затем выводятся жесткие ссылки и выполняется сборка.

PRO и CON

PRO Здесь мы получили легкую подпись в источнике автозагрузки, т.е. из сценариев или из bash r c файла (интерактивного), определения из автозагрузки () является скромным.

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

CONs Он захватывает номер сигнала, который не был бы необходим, если бы command_not_found_handle () была реальной функцией, вызываемой из контекста оболочки, это может произойти однажды.

Он реализован на bash функции, которая может двигаться (неправильно, $$ $ PPID), а затем нуждается в обслуживании на движущейся цели.

Заключение Эта реализация для меня в порядке (я Пофиг потерять SIGUSR1). Идеальным решением было бы, чтобы command_not_found_handle () была бы аккуратно реализована и затем вызывалась в контексте оболочки. Подобная реализация была бы возможна без какого-либо сигнала.

...