Bash variable scoping often confuses even experienced shell users. Variables feel local when they are written inside a function, but in Bash, they are global by default. This blog walks through that behavior step by step using simple, practical examples.
1. Defining a Function That Uses a Global Variable
Let’s start with a simple script that defines two functions. Notice that the variable name is assigned inside the function without using local.
[root@oel01db Shell-Scripting]# cat ./greetings
#!/bin/bash
greet() {
name=$1
echo "hello $name"
}
goodbye() {
name=$1
echo "goodbye $name"
}
[root@oel01db Shell-Scripting]#
At this point, nothing unusual yet. The real behavior shows up when we source this file and call the function from another script.
2. Creating Another Script That Uses the Function
Here is a second script that sources greetings and defines its own variable named name.
[root@oel01db Shell-Scripting]# cat ./global-local-var.sh
. ./greetings
name='Mahesh' # global variable
echo "before calling function greet, name = $name"
greet Ahana
echo "after calling function greet, name = $name"
[root@oel01db Shell-Scripting]#
Now let’s execute it.
3. Executing the Script
[root@oel01db Shell-Scripting]# ./global-local-var.sh
before calling function greet, name = Mahesh
hello Ahana
after calling function greet, name = Ahana
[root@oel01db Shell-Scripting]#
What Just Happened?
As you can see, the greet function modified the global variable name.
This happens because:
Variables in Bash are global by default
Even variables assigned inside a function are global unless explicitly declared
local
4. Step-by-Step Breakdown
. ./greetings
You source the file. Thegreetfunction is now loaded into your current shell.name='Mahesh'
You create a global variable calledname.greet Ahana
Inside the function, the linename=$1executes.Bash looks for a variable named
name. It finds the global one and overwrites it.
Since
nameis not declaredlocal, Bash effectively says:
“Oh, this is the global variable. I’ll overwrite it.”
When the function exits, the global variable has already been changed to
Ahana.
5. How to Fix This Problem
There are multiple correct ways to prevent functions from modifying global variables. Let’s explore them one by one.
Fix 1: Declare the Variable as local
The safest and most recommended solution.
[root@oel01db Shell-Scripting]# cat greetings
#!/bin/bash
greet() {
local name=$1
echo "hello $name"
}
goodbye() {
name=$1
echo "goodbye $name"
}
[root@oel01db Shell-Scripting]#
[root@oel01db Shell-Scripting]# ./global-local-var.sh
before calling function greet, name = Mahesh
hello Ahana
after calling function greet, name = Mahesh
[root@oel01db Shell-Scripting]#
Why This Works
localrestricts the variable’s scope to the functionOnce the function exits, the variable disappears
The global
nameremains untouched
Fix 2: Define the Function Using Parentheses ()
Functions defined with parentheses run in a subshell.
[root@oel01db Shell-Scripting]# cat greetings
#!/bin/bash
greet() (
name=$1
echo "hello $name"
)
goodbye() {
name=$1
echo "goodbye $name"
}
[root@oel01db Shell-Scripting]# ./global-local-var.sh
before calling function greet, name = Mahesh
hello Ahana
after calling function greet, name = Mahesh
[root@oel01db Shell-Scripting]#
Why This Works
Parentheses create a subshell
Variable changes do not propagate back to the parent shell
Fix 3: Call the Function Inside a Subshell
Instead of changing the function definition, you can run it inside a subshell.
[root@oel01db Shell-Scripting]# cat greetings
#!/bin/bash
greet() {
name=$1
echo "hello $name"
}
goodbye() {
name=$1
echo "goodbye $name"
}
[root@oel01db Shell-Scripting]#
[root@oel01db Shell-Scripting]# cat ./global-local-var.sh
. ./greetings
name='Mahesh'
echo "before calling function greet, name = $name"
(greet Ahana)
echo "after calling function greet, name = $name"
[root@oel01db Shell-Scripting]#
[root@oel01db Shell-Scripting]# ./global-local-var.sh
before calling function greet, name = Mahesh
hello Ahana
after calling function greet, name = Mahesh
[root@oel01db Shell-Scripting]#
6. Calling the Function Inside Curly Braces {}
Now let’s look at a common misconception.
[root@oel01db Shell-Scripting]# cat ./global-local-var.sh
. ./greetings
name='Mahesh'
echo "before calling function greet, name = $name"
{ greet Ahana; }
echo "after calling function greet, name = $name"
[root@oel01db Shell-Scripting]#
[root@oel01db Shell-Scripting]# ./global-local-var.sh
before calling function greet, name = Mahesh
hello Ahana
after calling function greet, name = Ahana
[root@oel01db Shell-Scripting]#
Why the Variable Changed Again
{}runs in the current shell, not a subshellSo global variables are modified
The curly brace
{is a reserved word, not a control operator. Because it is a word, it must be separated by spaces.
7. Using a Pipe Inside {}
[root@oel01db Shell-Scripting]# cat ./global-local-var.sh
. ./greetings
name='Mahesh'
echo "before calling function greet, name = $name"
{ greet Ahana | tr h m; }
echo "after calling function greet, name = $name"
[root@oel01db Shell-Scripting]#
[root@oel01db Shell-Scripting]# ./global-local-var.sh
before calling function greet, name = Mahesh
mello Amana
after calling function greet, name = Mahesh
[root@oel01db Shell-Scripting]#
Why Didn’t the Global Variable Change This Time?
Because:
Any command that is part of a pipeline runs in a subshell
Even though {} runs in the current shell, the presence of a pipe forces the function to execute in a subshell.
8. Pipeline with {} and an Outer Pipe
[root@oel01db Shell-Scripting]# cat ./global-local-var.sh
. ./greetings
name='Mahesh'
echo "before calling function greet, name = $name"
{ greet Ahana | tr h m; } | nl
echo "after calling function greet, name = $name"
[root@oel01db Shell-Scripting]#
[root@oel01db Shell-Scripting]# ./global-local-var.sh
before calling function greet, name = Mahesh
1 mello Amana
after calling function greet, name = Mahesh
[root@oel01db Shell-Scripting]#
9. Using () with a Pipeline
[root@oel01db Shell-Scripting]# cat ./global-local-var.sh
. ./greetings
name='Mahesh'
echo "before calling function greet, name = $name"
(greet Ahana | tr h m ) | nl
echo "after calling function greet, name = $name"
[root@oel01db Shell-Scripting]#
[root@oel01db Shell-Scripting]# ./global-local-var.sh
before calling function greet, name = Mahesh
1 mello Amana
after calling function greet, name = Mahesh
[root@oel01db Shell-Scripting]#
Key Takeaways
Bash variables are global by default
Use
localinside functions whenever possible()creates a subshell{}runs in the current shellAny pipeline forces a subshell, even inside
{}
If you remember just one rule:
If you don’t want to modify global state, use
localor a subshell.
No comments:
Post a Comment