1. Overview

Bash, the popular Unix shell, provides various built-in commands for managing and manipulating shell environment variables. Programs and processes can be influenced by these named values called environment variables.

Two commonly used commands for this purpose are set and export. In this tutorial, we’ll explore the differences between these two commands and their usage in Bash scripts.

Both set and export commands are compatible with most Unix-like shells. However, some of the options or behaviors may differ in some shells.

2. set Command in Bash

The set command in Bash lets us change the environment variables and options of the current shell session. We can use it to adjust the features and behavior of the shell, as well as to show the values of variables and functions.

The set command has a common syntax:

set [options] [arguments]

If we use set with no options or arguments, it’ll print a list of all variables and functions in the current shell. We can also use set to assign positional parameters.

The set command has many options that we can use to modify various aspects of the shell, such as error handling, debugging, expansion, and job control. Let’s see some of the most common options:

  • -e: exit right away if a command exits with a non-zero status
  • -u: treat unset variables as an error
  • -x: print commands and their arguments as they are run, mostly useful for debugging and tracing
  • -o: set or unset one of the shell options

In particular, the -e switch is useful for error handling and making sure that scripts don’t continue after a failure. To enable error handling in a script, we can use set -e at the start. We can also use set +e to turn off this option later in the script if we want to allow some commands to fail non-critically.

Overall, the set command is a powerful tool for customizing the behavior of the Bash shell, and understanding its options can be crucial for writing effective and robust shell scripts.

3. export Command in Bash

The export command is a built-in Bash command that we can use to create environment variables. Environment variables are global variables that are accessible by all processes running in the current shell session and its child processes.

In Bash, exported variables store data that can be shared and accessed by various scripts and commands. By marking a variable for export using the export command, we add it to the environment of the current session. This enables other scripts and commands to access its value.

3.1. How to Set Environment Variables

We can use the export command to define and assign values to environment variables. The export command has a common syntax:

$ export VARIABLE_NAME=VALUE

VARIABLE_NAME is the name of the environment variable, and VALUE is the value that we want to assign to the variable. Let’s say we want to create the USERNAME environment variable and assign it the value john:

$ export USERNAME=john
$ echo "$USERNAME"
john

Here, we also output the variable with echo. Importantly, this produces the same result if the variable is accessed by a child process of the current session.

3.2. Practical Use Cases for the export Command

There are various uses for the Bash export command. First, we can set global variables that other programs or scripts access. For example, we can use export PATH=$PATH:/home/user/bin to add a custom directory to our PATH variable, which determines where the shell looks for executable files.

Also, we can use export to make variables available to subprocesses. For example, the line export NAME=john in one script defines the NAME variable for any other script that runs within the same environment.

Another use case of export is to configure system-wide settings. We can use export to set environment variables in files such as ~/.bashrc or /etc/profile. The shell sources these files when it starts or when we log in. For example, we can use export LANG=en_US.UTF-8 in /etc/profile to set the default language and encoding for all users.

4. Differences Between set and export

Although both the set and export commands are used to manage environment variables and settings in Bash, there are some differences between them.

4.1. Example of export

To make the variable accessible through the child processes of the current shell, we use the export command and mark it as exported. Variables aren’t exported by default, which means only the current shell session can access them. For instance, let’s take the example of the export-set_1.sh script:

#!/usr/bin/env bash
me=great;                 # set the variable 'me' to 'great'
echo "$me";               # print out the variable
export me;                # export the variable to make it global
bash -c 'echo "$me"';     # print it out in a child process

Now, let’s run this script:

$ sh export-set_1.sh
great
great

In this example, we set the variable me to great and made it global by exporting it using export me. Then, we accessed a subshell using bash -c and demonstrated how the variable me is accessible within the subshell. This shows that export makes variables global, enabling child processes to access them.

4.2. Example of set

On the other hand, the set command sets shell attributes and positional parameters. Unlike export, set doesn’t export variables to child processes. Let’s see an example with the export-set_2.sh script:

#!/usr/bin/env bash
set me=great foo bar;
echo "$@";

Let’s run this script:

$ sh export-set_2.sh
me=great foo bar

In this example, we used the set command to set the positional parameters $1 to the value me=great, $2 to the value foo, and $3 to the value bar. Then, we used echo “$@” to print all the positional parameters to the terminal. Notably, great* isn’t assigned to the variable *me, and instead becomes a literal positional parameter. This showcases that set doesn’t export variables to child processes but rather sets values for use within the current shell session.

To summarize, we use the export command to make variables global, and we use the set command to set values to positional parameters for use within the current shell session.

4.3. Inheritance

Child processes and scripts can inherit variables defined with the export command. We can see that in the example scripts we demonstrated with export above.

In contrast, child processes or scripts don’t inherit positional parameters defined with the set command. They remain local to the script or shell session where they are defined and differ from the positional parameters of other processes.

Let’s modify the export-set_2.sh script to see this:

$!/usr/bin/env bash
set me=great foo bar;
echo "$@";  # print all the positional parameters

bash -c 'echo "$@"'; # print all the positional parameters in a child shell

​Let’s run this script:

$ sh export-set_2.sh
me=great foo bar

In this example, the empty line after the first output indicates that the child shell doesn’t inherit the positional parameters from the parent shell.

4.4. System Configuration With set

One of the major uses of the set command is to manipulate the system configuration for specific use in a script. It provides options to enable or disable error handling, debugging, and other shell behaviors. This is unique to the set command.

5. Common Pitfalls of Using set and export

While both set and export are useful commands that can help us modify the environment of a shell, they also have some drawbacks that we need to be aware of. In this section, we’ll discuss some common pitfalls and best practices for using set and export efficiently and safely.

5.1. Forgetting to Quote Variables

When using variables in Bash scripts, it’s important to enclose them in quotes to prevent issues with spaces and special characters. Quoting variables incorrectly can lead to unexpected behavior or errors:

#!/usr/bin/env bash

# Initialize variable with special characters
var="Hello * World!"

# Print variable without quotes
echo $var

# Output: Hello (and the files in the current directory)
# The output is unexpected because the shell interprets the asterisks as a pathname expansion

# Print variable with quotes
echo "$var"

# Output: Hello * World!
# The output is expected because the quotes preserve the special characters

In the above script, we didn’t quote the variable var when using it in the echo command. This leads to an unexpected error because the shell interpreted the asterisks. By enclosing the variable in quotes, we can prevent this issue and get the expected output.

5.2. Overwriting Built-in Variables

When naming variables, we should be careful not to use the same name as a built-in shell variable or function. Overwriting built-in variables can cause unexpected behavior and errors:

#!/usr/bin/env bash

echo "Current user: $USER" # Outputs the current user (e.g. "john")

# Overwrite the built-in variable USER with our own value
USER="hacker"
echo "Current user: $USER" # Outputs "hacker" instead of the actual user

# Restore the original value of USER
USER=$(/usr/bin/id -un)
echo "Current user: $USER" # Outputs the actual user again

In this script, we start by using the built-in $USER variable to output the current user. However, we then overwrite this variable with the value hacker, causing the subsequent output to be incorrect. Finally, we use the id command to get the actual value of $USER and restore it to its original value.

To avoid such errors, it’s important to use a naming convention that avoids clashing with built-in variables.

5.3. Overusing set options

When writing shell scripts, it’s important to choose the appropriate tools to catch errors and debug problems actively. However, overusing certain options can lead to unexpected behavior and make scripts harder to read.

For example, set -x can be useful for debugging, but it can clutter the output and make it harder to read. To prevent this, we use set -x only when necessary, and consider other debugging techniques, such as logging or using a debugger.

Let’s see an example of overusing set -x in a script:

#!/usr/bin/env bash

set -x                 # Turn on tracing
echo "This is a simple script"
x=10                   # Assign a value to x
y=20                   # Assign a value to y
z=$((x+y))             # Calculate the sum of x and y
echo "The sum of x and y is $z"
set +x                 # Turn off tracing

This script will print every command and its output to the standard error stream, which can be useful for debugging, but also very noisy and distracting. A better way to write this script would be to use set -x only for the parts that need debugging, and use explicit commands to output relevant information elsewhere:

#!/usr/bin/env bash
echo "This is a simple script"
x=10                   # Assign a value to x
y=20                   # Assign a value to y
set -x                 # Turn on tracing only for the calculation
z=$((x+y))             # Calculate the sum of x and y
set +x                 # Turn off tracing
echo "The sum of x and y is $z"

This script will only print the calculation part to the standard error stream, and print the rest to the standard output stream. This makes it easier to read and understand what the script is doing.

5.4. Using Uppercase Variable Names for Local Variables

Conventionally, we name environment variables with uppercase letters, while we don’t name local variables in uppercase. Using uppercase names for local variables can lead to confusion and naming conflicts. To prevent this, it’s usually best to use lowercase or mixed-case variable names for local variables.

5.5. Forgetting to Unset Variables

If we define a variable in a script and then no longer need it, failing to unset it can lead to wasted memory and potential naming conflicts. To prevent this, we’d use the unset command to remove variables when we no longer need them.

6. Conclusion

In this article, we used the set and export commands for different purposes and we observed their distinct differences and behavior.

In summary, we primarily use the set command to configure error handling, debugging, and script behavior within the local scope of a script or shell session. On the other hand, we use the export command to create global environment variables that other processes or scripts running in the same session can access.

Understanding the differences between set and export can be essential for effectively managing variables and settings in Bash scripts and Linux systems. By utilizing the right command in the appropriate context, we can ensure efficient and reliable script execution and system behavior.