bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

"unset var" pops var off variable stack instead of unsetting it


From: Stephane Chazelas
Subject: "unset var" pops var off variable stack instead of unsetting it
Date: Fri, 17 Mar 2017 21:51:34 +0000
User-agent: Mutt/1.5.24 (2015-08-30)

Hi,

consider this function:

split() (
  unset -v IFS  # default splitting
  set -o noglob # disable glob

  set -- $1 # split+(no)glob
  [ "$#" -eq 0 ] || printf '<%s>\n' "$@"
)

Note the subshell above for the local scope for $IFS and for
the noglob option. That's a common idiom in POSIX shells when
you want to split something: subshell, set IFS, disable glob,
use the split+glob operator.

split 'foo * bar'

outputs

<foo>
<*>
<bar>

as expected. So far so good.

Now, if that "split" functions is called from within a function
that declares $IFS local like:

bar() {
  local IFS=.
  split $1
}

Then, the "unset", instead of unsetting IFS, actually pops a
layer off the stack.

For instance

foo() {
  local IFS=:
  bar $1
}

foo 'a b.c:d'

outputs

<a b>

instead of

<a>
<b>

because after the "unset IFS", $IFS is not unset (which would
result in the default splitting behaviour) but set to ":" as it
was before "bar" ran "local IFS=."

A simpler reproducer:

$ bash -c 'f()(unset a; echo "$a"); g(){ local a=1; f;}; a=0; g'
0

Or even with POSIX syntax:

$ bash -c 'f()(unset a; echo "$a"); a=0; a=1 eval f'
0

A work around is to change the "split" function to:

split() (
  local IFS
  unset -v IFS  # default splitting
  set -o noglob # disable glob

  set -- $1 # split+(no)glob
  [ "$#" -eq 0 ] || printf '<%s>\n' "$@"
)

For some reason, in that case (when "local" and "unset" are
called in the same function context), unset does unset the
variable.

Credits to Dan Douglas
(https://www.mail-archive.com/miros-mksh@mirbsd.org/msg00707.html)
for finding the bug. He did find a use for it though (get the
value of a variable from the caller's scope).

-- 
Stephane



reply via email to

[Prev in Thread] Current Thread [Next in Thread]