1. Introduction
In Linux, we can execute shell scripts in two ways: as a separate process (subshell) or within the current shell environment. When a script runs as a subshell, it creates a new shell instance, and any changes made will not affect the parent shell. However, sourcing a script enables it to run within the current shell environment and modify local variables and data. To source a shell script, we can either use the source command or the dot (.) operator followed by the script’s path.
Sourcing scripts can be particularly useful when we need to set up a specific environment or define reusable functions in shell sessions. For example, we might create a script that sets up environment variables for a development project.
In this tutorial, we’ll cover how to define shell scripts that we can source within an existing Bash environment rather than execute as a subshell.
2. Using a File Name Convention
When we’re working with multiple shell scripts, it’s important to understand their intended purposes. Some scripts are designed to be executed directly, while others are meant to be sourced.
To avoid any confusion or potential issues, we can use a file name convention to make a clear distinction.
One approach that we can implement is to use the source keyword as a prefix while naming a file. For example, we can rename scripts such as nv_variable.sh and my_functions.sh to source_nv_variable.sh and source_my_function.sh respectively. This convention not only clarifies the purpose of each script but also reduces the risk of accidental execution.
Alternatively, we can rename scripts intended for sourcing by replacing the .sh extension with .src. For example, we can change the extension of my_file.sh to my_file.src. This is especially meaningful since many file-type associations expect an executable script to end with the .sh extension. Notably, the file extension itself doesn’t affect the functionality of the script. We can execute scripts regardless of their file name or extension.
The main purpose for changing the file name or the extension is to have a visual reminder to source the scripts instead of executing them.
3. Using a Logic Expression
One way we can define a shell script to be sourced is via a logical expression to set a flag variable:
$ cat source_check.sh
#!/bin/bash
(return 0 2>/dev/null) && sourced=1 || sourced=0
if [ $sourced -eq 1 ]; then
echo "This script is being sourced"
else
echo "This script is being executed"
fi
In this script, we use the return buildin to exit the script with a success status of 0. The 2>/dev/null redirects any error message to /dev/null that the return command may generate.
Next, we employ the && and || logical expressions to evaluate the condition.
When we execute the script directly, the sourced variable is set to 0, and the output reads This script is being executed. However, if we source the script, the sourced variable is set to 1, and the script prints This script is being sourced.
4. Using a String Comparison
When writing a shell script, we may want to ensure instead of just suggesting that the script can only be sourced rather than executed.
To enforce this behavior, we can include a check at the beginning of the script using a string comparison:
$ cat source_header.sh
#!/bin/sh
# This script should be sourced rather than executed
[[ "$0" = "${BASH_SOURCE[0]}" ]] || {
echo "Error: This script should be sourced rather than executed"
exit 1
}
# Rest of the script
echo "This script has been successfully sourced."
This script uses the conditional expression [[ “$0” = “${BASH_SOURCE[0]}” ]] to determine whether we sourced or directly executed it. The $0 variable holds the name of the shell script, such as source_header.sh, when called. The ${BASH_SOURCE[0]} is an array variable that contains the source path of the currently executing script.
The conditional expression compares the value of $0 and ${BASH_SOURCE[0]}. If they are equal, it means we directly executed the script, and the code block inside the curly braces {…} executes, thereby exiting the script. Otherwise, we sourced the script, and the rest of the code runs. In particular, we use the || logical OR operator.
5. Using a Function
An alternate approach is to use a function. By examining the name of the function that calls the current function, we can ensure that a shell script can only be sourced:
$ cat source_function.sh
#!/bin/sh
# Define a function to check if the script is being sourced
check_sourced() {
if [ "${FUNCNAME[1]}" != "source" ]; then
echo "Usage: source $0"
exit 1
fi
}
# Call the function to check if the script is being sourced
check_sourced
# Script code goes here
echo "This script is being sourced successfully."
This script defines a function named check_sourced that checks whether we’re sourcing or directly executing the function. Next, we use the ${FUNCNAME[1]} array variable, which stores the name of the function that called the current function.
If we source the script, ${FUNCNAME[1]} holds the value source. When executing the script directly, ${FUNCNAME[1]} has a different value or remains undefined.
Inside the check_sourced function, we compare the value of ${FUNCNAME[1]} with the string source. The function exits the script if they’re not equal, meaning we executed it directly.
However, if we sourced the script, the function continues execution and processes the rest of the script.
6. Conclusion
This article discusses three different methods to ensure sourcing shell scripts instead of executing them directly.
We covered the file naming method, a simple approach that we can easily adopt. The string comparison and function-based methods, on the other hand, provide more robust checks, ensuring that we cannot execute the script directly even if we try.