LukeMainFrame

Knowledge Is Power

Home  Blog Articles  Publications  About Me  Contacts  
20 May 2020

Variables in Bash

by Lord_evron

Let’s talk about variables in bash scripts. This isn’t a deep dive, but rather a clarification on this useful feature. What are bash variables, and why should a user or developer care? Simply put, they are variables that programs can access and read. Some are set by default and are called environmental variables. For example, if your script needs to access your home directory in Linux, which is usually /home/USERNAME, you could hardcode that path. However, this isn’t ideal, as it only works for one specific username. How can you access the path without knowing the username (besides using ~)? One solution is using the HOME environment variable. In your shell script, you can use $HOME, which will resolve to the correct path. But since you’re reading this, you probably already know this, so let’s move on.

Shell scripting

If you’ve done any shell scripting, you’ve likely encountered some nuances. For instance, what’s the difference between these:

GREET="Hello_"
# with no quotes
echo $GREET
>Hello_
# with single quotes
echo '$GREET'
>$GREET
# with double quotes
echo "$GREET"
>Hello_
# with curly braces
echo ${GREET}
>Hello_

Let’s examine each one, starting with the single quote example: echo '$GREET'. Bash treats characters within single quotes literally as a string, without attempting any interpretation.
So, echo '$GREET' prints $GREET. Use single quotes when you want to prevent any bash substitution. What about the other three commands?

With double quotes, bash does attempt variable substitution within the string. Consider this example:

SHELL=bash
# with no quotes we get error
echo pipe | is used to connect output in $SHELL
>Error
# with single quotes-- SHELL is not substituted
echo 'pipe | is used to connect output in $SHELL'
>pipe | is used to connect output in $SHELL
# with double quotes-- SHELL get substituted
echo "pipe | is used to connect output in $SHELL"
>pipe | is used to connect output in bash

The unquoted command results in an error because bash tries to pipe the commands “pipe” and “is” together using the | character. The single-quoted example prints the string literally, including “$SHELL”. Only the double-quoted version substitutes the variable, printing “pipe | is used to connect output in bash”. Also, be aware that unquoted strings in bash are subject to word splitting and globbing. Double quotes are often the correct choice. You can also prevent substitution for a specific variable using a backslash:

SHELL=bash 
# We can escape the substitution with \
echo "\$SHELL is set to $SHELL"
>$SHELL is set to bash

This will print “$SHELL is set to bash”. What about curly braces? They’re used to isolate variable names, especially in longer strings. For example:

GREET="Hello_"
# $GREETWorld is not set!
echo "$GREETWorld"
>
# Correct way
echo "${GREET}World"
>Hello_World

In the first echo, bash looks for a variable named GREETWorld, which doesn’t exist, resulting in an empty string. The second echo correctly uses curly braces to isolate the GREET variable, printing “Hello_World”.

SOURCE and EXPORT

Let’s discuss “source” and “export.” You might have used these keywords without fully understanding them. When you run a script, it’s executed in a new (child) shell. Why does this matter? Because variables in the child shell might be different. For instance:

# Create myscript.sh that echo VAR variable
echo 'echo $VAR' >> myscript.sh
# Create the VAR variable and make sure is there
VAR="Hello"
echo $VAR
>Hello
# Lets try to launch the script
./myscript.sh
>
# Lets try to use source to launch the script
source myscript.sh
>Hello
# . acts like source
. myscript.sh
>Hello
# . is not the same as ./
. ./myscript.sh
>Hello

We create a script that echoes the VAR variable (note the single quotes). We then set VAR in the current shell. However, when we execute the script normally (./myscript.sh), it doesn’t print the value because it’s running in a new shell where VAR is not defined. Using “source” (or .) runs the script in the current shell, allowing it to access VAR. Note that . is equivalent to source, but . (or source) is not the same as ./, which specifies a path and creates a subshell.

What if you want to access VAR from the child shell as well? That’s where “export” comes in. “export” makes a variable available to all child processes:

# Create myscript.sh that echo VAR2 variable
echo 'echo $VAR2' >> myscript.sh
# Set VAR2 and make sure that is there
VAR2="Hello"
echo $VAR2
>Hello
# Launch the script.. nothing is printed!
./myscript.sh
>
# Lets export the variable and try again! This time it works!
export VAR2
./myscript.sh
>Hello
# of course source still works as before
source ./myscript.sh
>Hello

We define VAR2 and echo it from a script. The first execution prints nothing. After exporting VAR2, the script correctly prints “Hello” because the child shell can now access it. source still works as before, as the current shell also retains the variable.

EVAL

“eval” is another useful command. Let’s revisit our script example and try something different:

# Lets create a txt file with a simple text
echo "Hello_world" >> hello.txt
# we create a variable that contains another variable. 
# MYFILE is not resolved because the single quotes
COMMAND='cat $MYFILE'
# Lets define MYFILE var
MYFILE="hello.txt"
# If we call COMMAND resolve to cat $MYFILE
$COMMAND
>cat: '$MYFILE': No such file or directory
# While this resolve to cat hello.txt
eval $COMMAND
>Hello_world

We create a text file “hello.txt” containing “Hello_world”. COMMAND stores the string “cat $MYFILE”. Because of the single quotes, $MYFILE is not immediately resolved. We then define MYFILE as “hello.txt”.

When we try to execute $COMMAND, it resolves to cat $MYFILE, which results in an error because there’s no file named literally $MYFILE. However, eval $COMMAND re-evaluates the content of COMMAND. This time, $MYFILE is substituted with “hello.txt,” and the cat command successfully prints “Hello_world”. This is useful when you have variable names stored within other variables. However, be aware that “eval” can be risky if not used carefully, as it can lead to unexpected behavior (also never use it with untrusted sources). It’s a powerful tool, but use it with caution.

We can And that is it for now… Happy Bash Coding…

tags: bash - code - linux - technology