Saturday, 7 February 2026

Understanding Global vs Local Variables in Bash

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. The greet function is now loaded into your current shell.

  • name='Mahesh'
    You create a global variable called name.

  • greet Ahana
    Inside the function, the line name=$1 executes.

  • Bash looks for a variable named name. It finds the global one and overwrites it.

Since name is not declared local, 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

  • local restricts the variable’s scope to the function

  • Once the function exits, the variable disappears

  • The global name remains 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 subshell

  • So 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 local inside functions whenever possible

  • () creates a subshell

  • {} runs in the current shell

  • Any pipeline forces a subshell, even inside {}

If you remember just one rule:

If you don’t want to modify global state, use local or a subshell.

No comments:

Post a Comment

JFrog Artifactory - How to install

JFrog Artifactory OSS Installation Guide CentOS 9 + PostgreSQL 17 This guide provides a structured workflow to install JFrog Artifactory OSS...