Локальные переменные в bash: локальные и подоболочки

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

Например:

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

or

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

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

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


person Ignitor    schedule 07.01.2011    source источник
comment
Но я мог представить, что создание подоболочки связано с накладными расходами. Запустите команду time на 1000 тестов и выясните накладные расходы, я думаю, что они малы или отсутствуют.   -  person Anders    schedule 07.01.2011


Ответы (2)


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

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

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

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

person Jonathan Leffler    schedule 07.01.2011

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

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

f() (
 echo "we are a 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"

Это выйдет

person Thomas    schedule 02.06.2016
comment
Вы можете обойти постоянную проверку кодов возврата/выхода, используя set -e. Теперь Bash будет выходить из вашего скрипта при сбое команды, в том числе когда подоболочка возвращает ненулевой код возврата/выхода. - person jmrah; 27.06.2020