У меня есть функция (=callee), которая должна эффективно объявлять и назначать пару переменных в своем вызывающем. Она также должна уметь определять имя своего вызывающего.
На данный момент я достигаю первого, возвращая запись в переданную переменную строки, которая будет eval
отредактирована вызывающей стороной.
write_local_var_assignments variable_name; eval "$variable_name"
Я полагаю, что последнего можно достичь, либо заставив вызывающую функцию передать функцию "$FUNCNAME"
, либо заставив вызываемую функцию вызвать caller
встроенную функцию и проанализировать ее вывод.
Все эти решения кажутся очень неуклюжими, поэтому у меня два вопроса:
- Может ли вызываемый объект назначить локальные переменные в контексте вызывающего объекта без его содействия?
Т.е., могу ли я сжать:
write_local_var_assignments variable_name; eval "$variable_name"
в просто
run_local_var_assignments
?
- Есть ли лучший способ получить имя вызывающей функции? Было бы неплохо получить результат напрямую, без разбора или подстановки команд.
решение1
В bash
(и ksh88
, mksh
, yash
, dash
, zsh
) область действия локальной переменной является динамической.
Этот код:
f() { a=2; echo "f: $a"; }
g() { local a=1; f; echo "g: $a"; }
a=0
g
echo "global: $a"
выдает следующий результат:
f: 2
g: 2
global: 0
Это переменная updates , f
поскольку она вызывается из .g
$a
g
Это контрастирует с переменными, объявленными с typeset
помощью функций, объявленных с помощью ksh
синтаксиса ( function f { ...; }
) в ksh93
, или переменными, объявленными с помощью private
, zsh
где вы получите:
f: 2
g: 1
global: 2
Так что в этом случае вам не нужно ничего делать.
Чтобы узнать имя вызывающей вас функции, в bash
, вы можете использовать${FUNCNAME[1]}
.
Эквивалент :zsh
$funcstack[2]
$ zsh -c 'f() { echo $funcstack[2]; }; g() { f; }; g'
g
$ bash -c 'f() { echo "${FUNCNAME[1]}"; }; g() { f; }; g'
g
решение2
Если вы не против использования подстановки команд, есть функция, которая позволит вам возвращать вызывающей стороне самооцененные строки, что позволит вам вызвать run_local_var_assignments
созданную вами функцию следующим образом:
$(run_local_var_assignments)
Функция выглядит так:
emit () {
local IFS=$'\n'
printf 'eval eval %q' "$*"
}
Он выдает оператор eval, очень похожий на ваш eval "$variable_name"
вышеприведенный. Поскольку это первое, что есть в результирующей строке, bash запускает выданный eval как команду.
Двойной eval и экранирование %q, выполняемые функцией, присутствуют там просто потому, что они необходимы для корректной передачи символов новой строки и других специальных символов вызывающей стороне.
Тогда ваша функция может быть записана следующим образом:
run_local_var_assignments () {
local assignments=()
assignments+=( 'local myvar1="my value1"' )
assignments+=( 'local myvar2="my value2"' )
emit "${assignments[@]}"
}
Задания записываются так, как если бы они были исходным кодом, поэтому такие вещи, как пробелы в значениях, требуют кавычек, как указано выше.
Вызов этой функции в подстановке команды (как в начале этого ответа) создаст эти локальные переменные в области действия вызывающего объекта с этими значениями.
Вы также можете использовать обычную строку или heredoc вместо массива операторов, но я часто нахожу метод массива полезным для динамического построения блока операторов, поэтому я решил показать это, хотя это и немного сложнее.