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.
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.
- -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.
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.
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.