Вопрос или проблема
Я прочитал то, что указано в библиографии относительно unset
, declare
, local
и “Функции оболочки”.
Моя версия Bash:
GNU bash, version 5.1.16(1)-release (x86_64-pc-linux-gnu)
var="outer"
declare -p var
unset var
declare -p var
function foo {
echo \""I'm in"\"
local var="inner"
declare -p var
unset var
declare -p var
}
foo
(странное оформление вокруг I'm in
нужно только для сохранения подсветки синтаксиса в следующем блоке текста.) Это выводит:
declare -- var="outer"
bash: declare: var: not found
"I'm in"
declare -- var="inner"
declare -- var
Существует разница между удалением глобальной переменной и удалением локальной переменной внутри функции. В первом случае переменная удаляется. Во втором случае переменная остается объявленной (но не инициализированной).
Существует ли способ полностью удалить локальную переменную внутри функции (до возврата функции)? То есть чтобы вывод был следующим:
declare -- var="outer"
bash: declare: var: not found
"I'm in"
declare -- var="inner"
bash: declare: var: not found
Это было бы полезно для проверки, существует ли переменная внутри функции, например, в:
function foo {
local var
while
declare -p var
do
echo "$var"
((var++))
[[ "$var" -eq 4 ]] \
&& unset var
done
}
Этот код бесконечно цикличен, выводя:
declare -- var="1"
1
declare -- var="2"
2
declare -- var="3"
3
declare -- var
declare -- var="1"
1
declare -- var="2"
2
declare -- var="3"
3
declare -- var
[опущено]
Есть ли что-то неправильное в проверке, существует ли переменная, используя declare -p
?
Единственное упоминание, которое я нашел в справочном руководстве по Bash о различии между удалением глобальной переменной и локальной, это:
Если переменная в текущей локальной области была удалена, она останется таковой (появляясь как не заданная) до тех пор, пока не будет сброшена в этой области или пока функция не вернется. (ссылка. Справочное руководство по Bash – раздел “Функции оболочки”)
где слово появляясь является единственной подсказкой.
Библиография
- Справочное руководство по Bash, раздел “4 Встроенные команды оболочки”
- Что делает unset?
Локальную переменную нельзя удалить в функции, в которой она была объявлена. Однако ее можно удалить в функции, вызываемой из этой функции.
#! /bin/bash
var="outer"
declare -p var
unset var
declare -p var
function local_unset {
unset "$1"
}
function foo {
echo "Я внутри"
local var="inner"
declare -p var
local_unset var
declare -p var
}
foo
Также обратите внимание, что Bash 5 добавил новую опцию оболочки localvar_unset
:
Если установлено, вызов
unset
для локальных переменных в предыдущих областях функции помечает их, так что последующие обращения находят их не заданными, пока эта функция не вернется. Это идентично поведению при удалении локальных переменных в текущей области функции.
Ответ или решение
Вопрос о том, как удалить локальную переменную внутри функции в Bash, является интересным аспектом работы с переменными в оболочке. Давайте подробнее разберем, как это можно сделать, и какие ограничения существуют в этой области.
Обзор работы переменных в Bash
В Bash переменные могут быть глобальными или локальными. Глобальные переменные доступны во всей сессии оболочки, тогда как локальные переменные существуют только в пределах функции, в которой они определены. Чтобы объявить переменную как локальную, используется конструкция local
.
Проблема с удалением локальной переменной
Когда вы используете команду unset
на локальной переменной внутри функции, переменная фактически становится "неопределенной", но она остается объявленной в рамках текущей функции. Это поведение отличается от удаления глобальной переменной, которая полностью исключается из контекста.
Вот пример, который вы привели:
function foo {
echo "I'm in"
local var="inner"
declare -p var
unset var
declare -p var
}
Этот код показывает, что после выполнения unset var
, переменная var
остается в области видимости (объявленной), но ее значение становится "unset".
Удаление локальной переменной — возможные решения
-
Использование вспомогательной функции: Одним из решений может быть создание вспомогательной функции для удаления переменных. Как вы упомянули, можно передать имя переменной в другую функцию:
function local_unset { unset "$1" } function foo { echo "I'm in" local var="inner" declare -p var local_unset var declare -p var } foo
В этом случае, при вызове
local_unset var
, переменная будет не просто "unset", а ее значение не будет доступно даже по вызовуdeclare -p
. -
Использование опции
localvar_unset
: В Bash версии 5 и новее добавлена опция оболочки под названиемlocalvar_unset
. При ее активации выполнениеunset
для локальных переменных в предыдущих контекстах сделает эти переменные недоступными до возврата из функции:shopt -s localvar_unset function foo { echo "I'm in" local var="inner" declare -p var unset var declare -p var } foo
С установленной опцией
localvar_unset
, вызовdeclare -p
покажет, что переменная больше не существует.
Воспроизводимость и бесконечный цикл
Ваш пример с бесконечным циклом также поднимает вопрос о правильности проверки на существование переменной:
function foo {
local var
while declare -p var; do
echo "$var"
((var++))
[[ "$var" -eq 4 ]] && unset var
done
}
В данном случае declare -p
будет возвращать, что переменная существует, пока она не была объявлена явно. В этой конструкции while
будет выполняться бесконечно, так как var
остается объявленным, даже если его значение становится неустановленным. В случае необходимости очистки значения вам нужно учитывать вызов unset
перед проверкой существования переменной.
Заключение
Удаление локальных переменных в Bash представляет собой уникальное поведение, требующее понимания разницы между локальными и глобальными переменными и их обработкой. Использование вспомогательных функций и опций оболочки может значительно упростить управление переменными и избежать частых недоразумений.
В качестве завершающего совета всегда следует учитывать поведение оболочки, а также внимательно тестировать функции и переменные в различных контекстах, чтобы избежать неожиданного поведения.