Stop bash functions clobbering global variables

0

Here's an extract from a bash script to reconfigure grub

    OLD="/etc/default/grub"
    NEW="/tmp/default.grub"
    SEC="1"

    cp "$OLD" "$NEW" || { >&2 echo "Couldn't copy $OLD" && exit 1; }

    sed -i "s/GRUB_TIMEOUT=[0-9]\+/GRUB_TIMEOUT=3/$SEC" "$NEW"

    cp_if_change_confirmed "$NEW" "$OLD"

    diff "$OLD" "$NEW" >/dev/null 2>&1 && \
    {
        CFG="$NEW.made"

        grub2-mkconfig --output="$CFG"

        echo "made $CFG from $NEW"

        cp_if_change_confirmed "$CFG" "/boot/grub2/grub.cfg"

        echo rm "$CFG"
        rm "$CFG"
    }

    echo rm "$NEW"
    rm "$NEW"

Those final echo and rm lines output

rm /tmp/default.grub.made
rm /tmp/default.grub.made
rm: cannot remove ‘/tmp/default.grub.made:‘ No such file or directory

Somehow NEW is being assigned the value of CFG.

Looking in cp_if_change_confirmed, which is a bash function, it contains

NEW="$1"

Which I guess explains the problem -- these variables don't have limited-enough scope.

But this is a maintenance problem -- I need to be able to include third party functions and know that they're not going to clobber values in the parent script.

Can I do this in bash, or is it a hopeless case and I just need to either be vigilant about variable names or use a real language?

spraff

Posted 2016-10-07T10:59:33.017

Reputation: 1 238

In functions, use local. A real language would make sense for larger scripts, with Perl or Ruby you don't have to go that far from the shell syntax. – choroba – 2016-10-07T11:03:26.377

Answers

1

Use local. Note that bash has only function scope (like var in JavaScript), not block scope.

myfunc() {
    local foo bar=$1 baz=$2
    foo=/tmp/$bar
    echo "value inside: $foo, $bar"
}
foo=123 bar=234
myfunc quux
echo "value outside: $foo, $bar"

If you have a block that doesn't need any persistence, you can call a subshell using ( ... ):

foo=123
( foo=345; echo "value inside: $foo" )
echo "value outside: $foo"

Note that subshells have the full overhead of fork() – they're independent processes. Also they don't let you choose which variables to localize – it's all or nothing.

user1686

Posted 2016-10-07T10:59:33.017

Reputation: 283 655