bash – как удалить локальную переменную (внутри функции)

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

Я прочитал то, что указано в библиографии относительно 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 – раздел “Функции оболочки”)

где слово появляясь является единственной подсказкой.

Библиография

Локальную переменную нельзя удалить в функции, в которой она была объявлена. Однако ее можно удалить в функции, вызываемой из этой функции.

#! /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".

Удаление локальной переменной — возможные решения

  1. Использование вспомогательной функции: Одним из решений может быть создание вспомогательной функции для удаления переменных. Как вы упомянули, можно передать имя переменной в другую функцию:

    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.

  2. Использование опции 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 представляет собой уникальное поведение, требующее понимания разницы между локальными и глобальными переменными и их обработкой. Использование вспомогательных функций и опций оболочки может значительно упростить управление переменными и избежать частых недоразумений.

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

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

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