4

I can't explain the following behavior of unset:

#!/bin/bash

my_unset() { unset "$@"; }

fn1() { local var;    unset var; var=1; }
fn2() { local var; my_unset var; var=2; }

var=0

fn1; echo "$var"
fn2; echo "$var"
0
2

edit: I would expect the result to be 1 2

It behaves that way on bash 3/4/5, is it a bug?


update

Here's an other example that shows that using my_unset doesn't make the variable global; it only removes the local variable from the current context:

#!/bin/bash

my_unset() { unset "$@"; }

fn1() { local var; my_unset var; var=1; }
fn2() { local var; fn1; echo "[fn2] var=$var"; }

var=0
fn2
echo "[global] var=$var"
[fn2] var=1
[global] var=0
6
  • Doing the unset in another function is somehow removing the localness of the variable. Commented Nov 11, 2022 at 23:41
  • @Barmar Yes, but it doesn't make it a global variable necessarily; I'll add an other example to the question. Commented Nov 11, 2022 at 23:45
  • Have you searched the bash bug list? Commented Nov 11, 2022 at 23:45
  • @Barmar Not yet; that would be a very long standing bug Commented Nov 11, 2022 at 23:46
  • 2
    I wonder if the output should be 1 2. local "creates a variable", then unset "removes the variable", so var=1 should refer to var at global scope. localvar_unset changes the behavior to 0 0. There is also this in docs If the unset acts on a variable at a previous scope, any instance of a variable with that name that had been shadowed will become visible, maybe this is expected behavior, if you unset in the current scope, it remains local. Commented Nov 11, 2022 at 23:48

1 Answer 1

4

See Chet's answer back in year 2012:

Back a number of years ago (16, to be exact), bash behaved like you expect. Between bash-1.14 and bash-2.0, I changed it in response to a number of bug reports that complained that a variable declared local in a function wasn't local when assigned a value in the function after being unset. Bash keeps a placeholder in the function's variable context so subsequent references to that unset variable don't traverse back through the call chain. This placeholder affects functions called by the function unsetting the local variable in the way you would expect dynamic scoping to work.

This only affects the current function, though: an unset issued from farther down the call chain, as you discovered, will unset the local variable without creating a placeholder.

Sign up to request clarification or add additional context in comments.

2 Comments

see how the cool upvar()/upvars() are using this feature: fvue.nl/wiki/Bash:_Passing_variables_by_reference
Nice find! I didn't remember that at all. :)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.