开发者

Increment a global variable in Bash

Here's a shell script:

globvar=0

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

myfunc
echo "something" | myfunc

echo "Global: $globvar"

When called, it prints out the following:

$ 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

The question is: why this happens and what behavior is correct?

P.S. I have a strange feeling that function behind the pipe is called in a forked shell... So, can there be a simple workaround?

P.P.S. This function is a simple test wrapper. It runs test application and analyzes its output. Then it increments $PASSED or $FAILED variable开发者_StackOverflow中文版s. Finally, you get a number of passed/failed tests in global variables. The usage is like:

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


Korn shell gives the same results as zsh, by the way.

Please see BashFAQ/024. Pipes create subshells in Bash and variables are lost when subshells exit.

Based on your example, I would restructure it something like this:

globvar=0

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

myfunc "$globvar"
globalvar=$(echo "something" | myfunc "$globalvar")


Piping something into myfunc in sh or bash causes a new shell to spawn. You can confirm this by adding a long sleep in myfunc. While it's sleeping call ps and you'll see a subprocess. When the function returns, that sub shell exits without changing the value in the parent process.

If you really need that value to be changed, you'll need to return a value from the function and check $PIPESTATUS after, I guess, like this:

globvar=0

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

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

echo "Global: $globvar"


The problem is 'which end of a pipeline using built-ins is executed by the original process?'

In zsh, it looks like the last command in the pipeline is executed by the main shell script when the command is a function or built-in.

In Bash (and sh is likely to be a link to Bash if you're on Linux), then either both commands are run in a sub-shell or the first command is run by the main process and the others are run by sub-shells.

Clearly, when the function is run in a sub-shell, it does not affect the variable in the parent shell (only the global in the sub-shell).

Consider adding an extra test:

echo Something | { myfunc; echo $globvar; }
echo $globvar
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜