Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 variables. 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"
like image 494
zserge Avatar asked Nov 23 '10 15:11

zserge


People also ask

Can we increment a global variable?

You can use $INCREMENT on a global variable or a subscript node of a global variable.

How do I increment a variable in bash?

Similar to other programming language bash also supports increment and decrement operators. The increment operator ++ increases the value of a variable by one. Similarly, the decrement operator -- decreases the value of a variable by one.

Can you do += in bash?

Bash is a widely used shell in Linux, and it supports the '+=' operator to concatenate two variables. As the example above shows, in Bash, we can easily use the += operator to concatenate string variables. Bash's += works pretty similar to compound operators in other programming languages, such as Java.

What does [- Z $1 mean in bash?

$1 means an input argument and -z means non-defined or empty. You're testing whether an input argument to the script was defined when running the script. Follow this answer to receive notifications.


2 Answers

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")
like image 72
Dennis Williamson Avatar answered Oct 06 '22 00:10

Dennis Williamson


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"
like image 37
Matt K Avatar answered Oct 06 '22 00:10

Matt K