Loop Variables and `unset` in Shell Script

Viewed 293

I just wanted to confirm here since I've only tested in dash shell, but do loop variables collide with variables in the outer scope in shell scripts in general? For example

#! /bin/sh

i='1 2 3'
a='a b c'

for i in $a; do
  echo "$i"
done

echo "$i"

This outputs:

a
b
c
c

which makes sense to me. That is, it seems to indicate that I'm right that loop variables will collide (they share the same namespace as the outer scope). I want to know because if I'm using an older-style shell that doesn't have the local command, I want to be sure to unset loop variables I use in functions. The texts I've read cover unset, but don't seem to cover this case.

Am I right?

2 Answers

You to avoid namespace issues .. You can fork your script and put the loop inside that fork ..

#! /bin/sh

i='1 2 3'
a='a b c'

function_to_fork(){
  for i in $a; do
     echo "$i"
  done
}

(function_to_fork)

echo "$i"

First: Yes, in all POSIX-compliant shells, variables are global by default, and loops do not have their own scope.

To prevent variables you use from escaping to global context, encapsulate the usage in a function with a local declaration, as follows:

i='1 2 3'
a='a b c'

yourfunc() {
  local i             # <- here, we make i function-local
  for i in $a; do     # aside: don't use unquoted expansions like this
     echo "$i"
  done
}

yourfunc
echo "$i"

...and $i is no longer overwritten.


local is not part of the POSIX sh specification, but it's such a widely-honored extension that even ash and dash provide it.

Related