Отложенное раскрытие составной переменной в Bash - PullRequest
0 голосов
/ 09 июля 2020

Я определяю переменную как композицию других переменных и некоторого текста, и я пытаюсь заставить эту переменную не расширять содержащиеся в ней переменные при назначении. Но я хочу, чтобы он расширился, когда позвонят позже. Таким образом, я мог бы повторно использовать один и тот же шаблон для печати разных результатов, поскольку внутренние переменные продолжают меняться. Я стараюсь избегать eval в максимально возможной степени, поскольку я буду получать некоторые внутренние переменные от третьих лиц, и я не знаю, чего ожидать.

Мой вариант использования, как показано ниже, иметь некоторый «стек вызовов», чтобы я мог регистрировать все сообщения в том же формате и вести запись сценария, функции и строки зарегистрированного сообщения в некотором формате, например: script.sh:this_function:42.

Моя попытка решения

called.sh:

#!/bin/bash

SCRIPT_NAME="`basename "${BASH_SOURCE[0]}"`"
CURR_STACK="${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"


echo "${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"
echo "${CURR_STACK}"
echo

function _func_1 {
    echo "${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"
    echo "${CURR_STACK}"
}
_func_1

Итак, я намерен получить те же результаты при печати "${CURR_STACK}", что и при печати предыдущей строки.

Если есть какой-то встроенный или другой умный способ зарегистрировать этот «стек вызовов», обязательно дайте мне знать! Я с радостью помашу своим кодом на прощание, но мне все равно хотелось бы знать, как предотвратить расширение переменных сразу после присвоения CURR_STACK, но при этом сохранить их возможность расширяться дальше.

Мне не хватает shopt?

Что я пробовал:

Случай 1 (расширяется в строке 4):

CURR_STACK="${SCRIPT_NAME}:${FUNNAME[0]}:${LINENO[0]}"
CURR_STACK="`echo "${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}"`"
CURR_STACK="`echo "\${SCRIPT_NAME}:\${FUNCNAME[0]}:\${LINENO[0]}"`"
called.sh::7              <------------------| These are control lines
called.sh::4  <---------------. .------------| With the results I expect to get.
                               X
called.sh:_func_1:12      <---´ `-------| Both indicate that the values expanded
called.sh::4  <-------------------------| on line 4 - when CURR_STACK was set.

Случай 2 (без расширения):

CURR_STACK="\${SCRIPT_NAME}:\${FUNNAME[0]}:\${LINENO[0]}"
CURR_STACK=\${SCRIPT_NAME}:\${FUNCNAME[0]}:\${LINENO[0]}
CURR_STACK="`echo '${SCRIPT_NAME}:${FUNCNAME[0]}:${LINENO[0]}'`"
called.sh::7
${SCRIPT_NAME}:${FUNNAME[0]}:${LINENO[0]}  <-------.----| No expansion at all!...
                                                  / 
called.sh::12                                    /
${SCRIPT_NAME}:${FUNNAME[0]}:${LINENO[0]}  <----´

Ответы [ 2 ]

2 голосов
/ 09 июля 2020

Переменные оболочки - это простой инертный текст (*), а не исполняемый код; здесь нет никакой концепции отложенной оценки. Чтобы сделать что-то, что при использовании делает что-то , создайте функцию вместо переменной:

print_curr_stack() {
    echo "$(basename "${BASH_SOURCE[1]}"):${FUNCNAME[1]}:${BASH_LINENO[0]}"
}
# ...
echo "We are now at $(print_curr_stack)"
# Or just run it directly:
print_curr_stack

Примечание: использование BASH_SOURCE[1] и FUNCNAME[1] позволяет получить информацию о контексте, в котором функция была запущена откуда, а не где он находится в самой функции. Но по какой-то причине я не совсем понимаю, BASH_LINENO[1] получает неправильную информацию, а BASH_LINENO[0] - это то, что вам нужно.

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

print_curr_stack() {
    echo "$@" "$(basename "${BASH_SOURCE[1]}"):${FUNCNAME[1]}:${BASH_LINENO[0]}"
}
# ...
print_curr_stack "We are now at"

(* Из того, что я сказал о переменных, есть исключение, они просто содержат инертный текст: некоторые переменные - например, $LINENO, $RANDOM, и т.д. c - обрабатываются оболочкой специально. сам по себе. Но вы не можете создать новые, как это, кроме как путем изменения самой оболочки.)

0 голосов
/ 09 июля 2020

Вы знакомы с eval?

$ a=this; b=is; c=a; d=test;
$ e='echo "$a $b $c $d"';
$ eval $e;
this is a test

$ b='is NOT';  # modify one of the variables
$ eval $e;
this is NOT a test

$ f=$(eval $e);  # capture the value of the "eval" statement
$ echo $f;
this is NOT a test
...