Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are bash variables not global if sourced script runs in a function?

Tags:

scope

bash

Sourcing a file normally from another script, I can access its variables.

If I source a script from within a function, its variables aren't global, which seems to contradict the manpage:

FUNCTION Variables local to the function may be declared with the local builtin command. Ordinarily, variables and their values are shared between the function and its caller.

source filename [arguments] Read and execute commands from filename in the current shell environment

Happens with all my conveniently available versions: 3.2.57(1)-release (x86_64-apple-darwin17), 4.3.42(1)-release (x86_64-suse-linux-gnu), and version 4.3.48(1)-release (x86_64-pc-linux-gnu)

test-sourced.sh:

#!/bin/bash
echo $BASH_SOURCE $$ $SHLVL
declare -x FOO=bar
foo() { echo funfoo=$FOO $$ $SHLVL ; }

test-top.sh:

#!/bin/bash
echo $BASH_SOURCE $$ $SHLVL

funcsource () { source ./test-sourced.sh ; }
echo ==== funcsource...
funcsource
echo foo=$FOO
foo

echo ==== source...
source ./test-sourced.sh
echo foo=$FOO
foo

I see this output, but expected to see both funcsource and source do the same thing:

$ ./test-top.sh 
./test-top.sh 1234 2
==== funcsource...
./test-sourced.sh 1234 2
foo=
funfoo= 1234 2
==== source...
./test-sourced.sh 1234 2
foo=bar 1234 2
funfoo=bar

It's the same PID and the same shell level, so it looks like deliberate behaviour. Is this a bug, or am I missing something?


Update: echoing $FOO and running 'foo' in the function immediately after the source command DOES give their values, so they're getting that far but are for some reason kept local to the function scope. Which still seems to contradict the manual.

like image 241
android.weasel Avatar asked Apr 11 '18 10:04

android.weasel


People also ask

Are variables in bash functions global?

Global variables They are visible and valid anywhere in the bash script. You can even get its value from inside the function. If you declare a global variable within a function, you can get its value from outside the function.

What happens when you source a bash script?

The source Command The built-in bash source command reads and executes the content of a file. If the sourced file is a bash script, the overall effect comes down to running it. We may use this command either in a terminal or inside a bash script.

How do you set a global variable in bash?

The easiest way to set environment variables in Bash is to use the “export” keyword followed by the variable name, an equal sign and the value to be assigned to the environment variable.


1 Answers

The reason for this behaviour is because you’re using the declare shell builtin. According to help declare:

When used in a function, declare makes NAMEs local, as with the local command. The -g option suppresses this behavior.

Changing test-sourced.sh to use declare -g instead of declare -x (export the variable to the environment) – or a regular shell variable assignment – should show the expected behaviour (where the variable is global):

#!/bin/bash
echo $BASH_SOURCE $$ $SHLVL

# Simple shell variable assignment (global by default)
FOO=bar

# Use declare to globally assign a value to the shell variable
declare -g FOO=bar

foo() { echo funfoo=$FOO $$ $SHLVL ; }

Exporting the variable to the environment only adds utility if you want the variable to be accessible to future child processes started from that shell session. If this is what you want, you can use either of the following Bash constructs to ensure the variable is both global and exported:

export FOO=bar
declare -x -g FOO=bar
like image 62
Anthony Geoghegan Avatar answered Sep 30 '22 17:09

Anthony Geoghegan