Локальные переменные в bash: local vs subshell

насколько я знаю, есть два способа создать локальные переменные в функции bash: создать подрешетку или объявить каждую переменную как локальную.

например:

# using local
function foo
{
  local count
  for count in $(seq 10)
  do
    echo $count
  done
}

или

# using subshell
function foo
{
  (
    for count in $(seq 10)
    do
      echo $count
    done
  )
}

очевидно, что версия, использующая подрешетку, проще писать, потому что вам не нужно заботиться о объявлении всех переменных локальными (не говоря уже о переменных среды), созданных/экспортированных такими инструментами, как команде getopts). Но я могу представить, что создание у subshell есть накладные расходы.

Так какой же подход лучше? Каковы плюсы и минусы?

2 ответов


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

Если ваш скрипт будет активно использоваться, и производительность действительно имеет значение (так что у вас будут сотни пользователей, которые будут запускать его одновременно, много раз в день), тогда вы можете беспокоиться о стоимости производительности суб-оболочки. OTOH, если вы запускаете его раз в месяц и сценарий в целом работает менее 10 секунд, вы, вероятно, не будет.

однако, с точки зрения ясности, гораздо лучше быть явным и объявлять переменные - это уменьшает риск взлома скрипта, потому что кто-то приходит и говорит: "Эта под-оболочка явно не нужна" (и это действительно не так; я хотел бы удалить под-оболочки из ваших функций).

посмотрите на эволюцию скриптов Perl. Они начали как free-for-all с переменными, входящими в существование по требованию. Они постепенно стали более строгими,с нормальным стилем, который теперь должен предшествовать всем переменным. В какой - то степени снаряды следовали аналогичному пути, но не так строго, как Perl. Awk также является интересным примером; его функции используют глобальные переменные, если они не являются аргументами функции, что приводит к написанию функций с 3 активными аргументами (скажем) и 5 неактивными аргументами, которые эффективно определяют локальные переменные. Это немного эксцентрично, хотя это "работает".


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

Я думаю, что это очень подвержено ошибкам и предпочитает всегда использовать функции subshell:

f() (
 echo "we are a subshell"
)

не нужно объявлять локальные переменные, но и нет способа изменить глобальные переменные. Что, на мой взгляд, хорошо!

одним из дополнительных последствий является то, что вам всегда нужно проверять код возврата / выхода таких функций и действовать соответственно! Это потому, что ты не удается выйти из скрипта из функции subshell!

f() (
   echo "Trying to exit"
   exit 1
)

f
echo "Did not exit"

Это не выйдет из вашего сценария. Вам нужно сделать это так:

f() (
   echo "Trying to exit"
   exit 1
)

f || exit $?
echo "Did not exit"

это выйдет