Подтвердить что ты не робот

POSIX-совместимый способ изменения переменных для функции в оболочке Script

Есть ли способ, совместимый с POSIX, ограничить область действия переменной функцией, в которой она объявлена? то есть:.

Testing()
{
    TEST="testing"
}

Testing
echo "Test is: $TEST"

должен печатать "Test is:". Я читал о объявлении, локальном и наборе ключевых слов, но не похоже, что они необходимы для встроенных модулей POSIX.

4b9b3361

Ответ 1

Обычно выполняется с ключевым словом local, которое, как вам кажется, не определено POSIX. Вот информативное обсуждение о добавлении "local" в POSIX.

Однако даже самая примитивная POSIX-совместимая оболочка, о которой я знаю, которая используется некоторыми дистрибутивами GNU/Linux в качестве /bin/sh по умолчанию, dash (Debian Almquist Shell), поддерживает ее. FreeBSD и NetBSD используют ash, оригинальную оболочку Almquist, которая также поддерживает ее. OpenBSD использует реализацию ksh для /bin/sh, которая также поддерживает его. Поэтому, если вы не нацелены на поддержку не-GNU-систем, отличных от BSD, таких как Solaris, или тех, которые используют стандартный ksh и т.д., Вы можете избежать использования local. (Возможно, стоит поставить комментарий прямо в начале script, ниже строки shebang, отметив, что это не строго POSIX sh script. Просто чтобы не быть злым.) Сказав все это, вы можете захотеть для проверки соответствующих man-страниц всех этих реализаций sh, поддерживающих local, поскольку они могут иметь тонкие различия в том, как именно они работают. Или просто не используйте local:

Если вы действительно хотите полностью соответствовать POSIX или не хотите вмешиваться в возможные проблемы, и поэтому не используйте local, то у вас есть пара вариантов. Ответ, данный Ларсом Бринкхоффом, звучит, вы можете просто обернуть функцию в суб-оболочку. Однако это может иметь и другие нежелательные эффекты. Кстати, грамматика оболочки (для POSIX) позволяет:

my_function()
(
  # Already in a sub-shell here,
  # I'm using ( and ) for the function body and not { and }.
)

Хотя возможно, чтобы это было супер-портативным, некоторые старые оболочки Bourne могут быть даже не совместимы с POSIX. Просто хотел упомянуть, что POSIX позволяет это.

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

Последняя и сумасшедшая идея, о которой я могу думать, - это реализовать local самостоятельно. Оболочка имеет eval, которая, каким бы злым она ни была, уступает некоторым безумным возможностям. Следующее в основном реализует динамическое масштабирование старых Lisps, я буду использовать ключевое слово let вместо local для дальнейших холодных точек, хотя в конце вы должны использовать так называемый unlet:

# If you want you can add some error-checking and what-not to this.  At present,
# wrong usage (e.g. passing a string with whitespace in it to `let', not
# balancing `let' and `unlet' calls for a variable, etc.) will probably yield
# very very confusing error messages or breakage.  It also very dirty code, I
# just wrote it down pretty much at one go.  Could clean up.

let()
{
    dynvar_name=$1;
    dynvar_value=$2;

    dynvar_count_var=${dynvar_name}_dynvar_count
    if [ "$(eval echo $dynvar_count_var)" ]
    then
        eval $dynvar_count_var='$(( $'$dynvar_count_var' + 1 ))'
    else
        eval $dynvar_count_var=0
    fi

    eval dynvar_oldval_var=${dynvar_name}_oldval_'$'$dynvar_count_var
    eval $dynvar_oldval_var='$'$dynvar_name

    eval $dynvar_name='$'dynvar_value
}

unlet()
for dynvar_name
do
    dynvar_count_var=${dynvar_name}_dynvar_count
    eval dynvar_oldval_var=${dynvar_name}_oldval_'$'$dynvar_count_var
    eval $dynvar_name='$'$dynvar_oldval_var
    eval unset $dynvar_oldval_var
    eval $dynvar_count_var='$(( $'$dynvar_count_var' - 1 ))'
done

Теперь вы можете:

$ let foobar test_value_1
$ echo $foobar
test_value_1
$ let foobar test_value_2
$ echo $foobar
test_value_2
$ let foobar test_value_3
$ echo $foobar
test_value_3
$ unlet foobar
$ echo $foobar
test_value_2
$ unlet foobar
$ echo $foobar
test_value_1

(Кстати, unlet может быть задано любое количество переменных сразу (как разные аргументы), для удобства, не показано выше.)

Не пробуйте это дома, не показывайте его детям, не показывайте его своим сотрудникам, не показывайте его на #bash в Freenode, не показывайте его членам Комитет POSIX, не показывайте это мистеру Борну, возможно, покажите его отцу Маккарти, чтобы рассмешить его. Вы были предупреждены, и вы не узнали об этом от меня.

EDIT:

По-видимому, меня избили, отправив бот IRC greybot на Freenode (принадлежит #bash), команда "posixlocal" заставит дать один неясный код, который демонстрирует способ достижения локальных переменных в POSIX sh, Вот несколько очищенная версия, потому что оригинал был трудно расшифровать:

f()
{
    if [ "$_called_f" ]
    then
        x=test1
        y=test2
        echo $x $y
    else
        _called_f=X x= y= command eval '{ typeset +x x y; } 2>/dev/null; f "[email protected]"'
    fi
}

Эта транскрипция демонстрирует использование:

$ x=a
$ y=b
$ f
test1 test2
$ echo $x $y
a b

Таким образом, он позволяет использовать переменные x и y как локальные в ветки then формы if. В ветки else можно добавить больше переменных; обратите внимание, что их необходимо добавить дважды, как только variable= в исходном списке, и один раз передан как аргумент typeset. Обратите внимание, что не требуется unlet или так (это "прозрачная" реализация), и никакие манипуляции с именами и чрезмерные eval не выполняются. Таким образом, это, по-видимому, намного более чистая реализация.

ИЗМЕНИТЬ 2:

Выходит typeset не определяется POSIX, а реализации оболочки Almquist (FreeBSD, NetBSD, Debian) не поддерживают его. Таким образом, вышеупомянутый хак не будет работать на этих платформах.

Ответ 2

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

например. попробуйте это

foo()
{
  ( x=43 ; echo $x )
}

x=42
echo $x
foo
echo $x

Ответ 3

Вот функция, которая позволяет определять область видимости:

scope() {
  eval "$(set)" command eval '\"\[email protected]\"'
}

Пример script:

x() {
  y='in x'
  echo "$y"
}
y='outside x'
echo "$y"
scope x
echo "$y"

Результат:

outside x
in x
outside x

Ответ 4

Определите функции с помощью синтаксиса function myfunc { и используйте typeset myvar для определения ваших переменных. Если вы определяете такие функции таким образом, а не используете синтаксис myfunc(){, все общие оболочки Bourne (bash, zsh, ksh '88 и '93) будут локализовать переменные, определенные с помощью typeset (и псевдонимы, такие как integer).

Или изобрести колесо. Какой бы ни плавает ваша лодка.;)

EDIT: в то время как вопрос запрашивает POSIX, и это не синтаксис определения функции, совместимый с POSIX, пользователь, который спросил, указывает в более позднем комментарии, что он использует bash. Использование "typset" в сочетании с синтаксисом определения альтернативных функций является лучшим решением там, так как для истинного механизма POSIX требуются дополнительные накладные расходы на разворачивание новой подоболочки.