Увеличение глобальной переменной в Bash - PullRequest
10 голосов
/ 23 ноября 2010

Вот сценарий оболочки:

globvar=0

function myfunc {
  let globvar=globvar+1
  echo "myfunc: $globvar"
}

myfunc
echo "something" | myfunc

echo "Global: $globvar"

При вызове он выводит следующее:

$ sh zzz.sh
myfunc: 1
myfunc: 2
Global: 1
$ bash zzz.sh
myfunc: 1
myfunc: 2
Global: 1
$ zsh zzz.sh
myfunc: 1
myfunc: 2
Global: 2

Вопрос в том, почему это происходит и какое поведение правильно?

PS У меня странное ощущение, что функция за трубой вызывается в раздвоенной оболочке ... Итак, может ли быть простой обходной путь?

PPS Эта функция представляет собой простую тестовую оболочку.Он запускает тестовое приложение и анализирует его вывод.Затем он увеличивает переменные $ PASSED или $ FAILED.Наконец, вы получаете ряд пройденных / неудачных тестов в глобальных переменных.Использование как:

test-util << EOF | myfunc
input for test #1
EOF
test-util << EOF | myfunc
input for test #2
EOF
echo "Passed: $PASSED, failed: $FAILED"

Ответы [ 4 ]

7 голосов
/ 23 ноября 2010

Кстати, оболочка Korn дает те же результаты, что и zsh.

Пожалуйста, смотрите BashFAQ / 024 . Каналы создают в Bash подоболочки, и при выходе из них они теряются.

Исходя из вашего примера, я бы перестроил его примерно так:

globvar=0

function myfunc {
    echo $(($1 + 1))
}

myfunc "$globvar"
globalvar=$(echo "something" | myfunc "$globalvar")
3 голосов
/ 23 ноября 2010

При добавлении чего-либо в myfunc в sh или bash появляется новая оболочка.Вы можете подтвердить это, добавив длинный сон в myfunc.Пока он спит, звоните ps, и вы увидите подпроцесс.Когда функция завершает работу, эта вложенная оболочка завершается без изменения значения в родительском процессе.

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

globvar=0

function myfunc {
  let globvar=globvar+1
  echo "myfunc: $globvar"
  return $globvar
}

myfunc
echo "something" | myfunc
globvar=${PIPESTATUS[1]}

echo "Global: $globvar"
0 голосов
/ 23 ноября 2010

Проблема в том, «какой конец конвейера, использующего встроенные модули, выполняется исходным процессом?»

В zsh похоже, что последняя команда в конвейере выполняется сценарием основной оболочки, когдакоманда является функцией или встроенной.

В Bash (и sh, вероятно, будет ссылкой на Bash, если вы работаете в Linux), либо обе команды выполняются в под-оболочке, либопервая команда запускается основным процессом, а остальные - вложенными оболочками.

Очевидно, что когда функция запускается в вспомогательной оболочке, она не влияет на переменную в родительской оболочке (толькоglobal в вложенной оболочке).

Попробуйте добавить дополнительный тест:

echo Something | { myfunc; echo $globvar; }
echo $globvar
0 голосов
/ 23 ноября 2010

Используйте export вместо let, в противном случае переменная является локальной (используйте $ (()) для выполнения арифметических операций)

export globvar=0

function myfunc {
  export globvar=$((globvar+1))
  echo "myfunc: $globvar"
}
...