Bash
choosing between 0 and BASHSOURCE
In the realm of shell scripting, understanding how to reference the script’s name and location is crucial for creating robust and maintainable code. Two common variables used for this purpose are $0 and BASH_SOURCE. While both seem to offer similar functionality – identifying the script being executed – subtle yet significant differences exist. Choosing between $0 and BASH_SOURCE depends heavily on the context of your script, particularly when dealing with sourced files, symbolic links, or complex script structures. This article delves into the nuances of each variable, providing clear examples and guidance to help you make informed decisions and write more reliable shell scripts. We’ll explore scenarios where one might be preferred over the other, ensuring your scripts behave as expected in various environments. Understanding these distinctions will improve your scripting skills and lead to more predictable and maintainable scripts.
Understanding $0: The Script Invocation Name
The $0 variable in Bash is a fundamental part of shell scripting. It represents the name of the script as it was invoked. This means that the value of $0 is determined by how the script was executed, not necessarily its physical location on the file system. For instance, if you execute a script named “my_script.sh” by simply typing ./my_script.sh, then $0 will likely contain “./my_script.sh”. However, if you execute it using an absolute path like /path/to/my_script.sh, then $0 will reflect that absolute path. This behavior makes $0 useful for displaying the script’s invocation name in logs or error messages. According to the Bash manual, $0 “expands to the name of the shell or shell script.”
One common use case for $0 is in providing help or usage information when a script is run with incorrect arguments. By including $0 in the usage message, you can clearly show the user how the script should be invoked. For example, a script might check the number of arguments passed and, if they are insufficient, print a message like “Usage: $0 $0 can be problematic in certain scenarios, especially when dealing with symbolic links or scripts that are sourced rather than executed directly. In such cases, $0 might not provide the information you expect about the script’s actual location or identity. As a reference, consider the information available on the GNU Bash manual page here.
Furthermore, $0 can be modified within the script itself, potentially leading to unexpected behavior if not handled carefully. While this is not a common practice, it’s something to be aware of. The core functionality of $0 is tied to how the script is called, making it susceptible to variations in invocation. For instance, if a user executes a script using a relative path from a different directory, $0 will reflect that relative path, which may not be what the script author intended. Therefore, while $0 is a valuable tool, it’s essential to understand its limitations and consider alternative approaches, such as BASH_SOURCE, for more reliable script identification.
Delving into BASH_SOURCE: The Source of Truth
BASH_SOURCE, on the other hand, provides a more reliable way to determine the source file of a script, especially when dealing with sourced scripts. Unlike $0, BASH_SOURCE is an array variable. The first element, ${BASH_SOURCE[0]}, contains the name of the current source file. This is particularly useful when a script sources other scripts, as BASH_SOURCE will accurately reflect the location of the sourced file, regardless of how the main script was invoked. This distinction makes BASH_SOURCE invaluable for managing dependencies and ensuring that scripts can locate necessary files and resources.
One of the key advantages of BASH_SOURCE is its ability to handle symbolic links correctly. When a script is executed via a symbolic link, $0 will reflect the name of the symbolic link, while ${BASH_SOURCE[0]} will point to the actual file being executed. This is crucial for scripts that need to determine their true location for tasks such as loading configuration files or accessing data directories. For example, consider a script located at /path/to/real_script.sh and a symbolic link /usr/bin/my_script pointing to it. When my_script is executed, $0 will be /usr/bin/my_script, while ${BASH_SOURCE[0]} will be /path/to/real_script.sh. According to the article “Understanding the Bash BASH_SOURCE Variable” here, BASH_SOURCE is the preferred method for accessing the location of the script.
Furthermore, BASH_SOURCE plays a critical role in modular scripting, where scripts are designed to be sourced into other scripts. In this scenario, $0 would refer to the main script, not the sourced script. However, ${BASH_SOURCE[0]} within the sourced script will correctly identify the sourced script’s location. This enables the sourced script to perform actions relative to its own location, such as loading configuration files or defining functions specific to that module. This functionality is essential for creating reusable and maintainable script libraries. In summary, BASH_SOURCE offers a more reliable and context-aware way to determine the source file of a script, especially in complex scripting environments.
Practical Use Cases: When to Choose Which
The choice between $0 and BASH_SOURCE depends on the specific requirements of your script. If you simply need to display the script’s invocation name or provide basic usage information, $0 might suffice. However, for more complex scenarios, particularly those involving sourced scripts, symbolic links, or modular designs, BASH_SOURCE offers a more robust and reliable solution. Let’s consider some practical examples to illustrate these differences.
Imagine a script that needs to load a configuration file located in the same directory as the script itself. If the script is executed via a symbolic link, using $0 to determine the script’s location would lead to incorrect results, as it would point to the symbolic link rather than the actual script file. In this case, ${BASH_SOURCE[0]} would provide the correct path, allowing the script to load the configuration file successfully. Another scenario involves a script that sources multiple modules. Each module might need to perform actions relative to its own location. Using BASH_SOURCE within each module ensures that it operates correctly, regardless of where the main script is located or how it was invoked. As stated in “Bash Scripting: $0 vs. BASH_SOURCE” here, understanding how these variables behave is essential for writing maintainable scripts.
Here’s a breakdown to help you decide:
- Use
$0when you need to display the script’s invocation name in a simple context. - Use
BASH_SOURCEwhen you need to determine the actual location of a script, especially when it’s sourced or executed via a symbolic link. - Use
BASH_SOURCEfor modular scripts where each module needs to operate relative to its own location.
By understanding these distinctions and considering the specific requirements of your script, you can choose the appropriate variable and ensure that your script behaves as expected in various environments. Furthermore, you can improve the maintainability and reliability of your scripts, making them easier to debug and update in the future.
Best Practices and Examples
To further illustrate the differences and best practices for using $0 and BASH_SOURCE, let’s examine some code examples. Suppose you have a script named “main.sh” that sources another script named “module.sh”. Within “module.sh”, you need to determine its own location to load a configuration file. Here’s how you could use BASH_SOURCE to achieve this:
module.sh:
!/bin/bash MODULE_DIR="$(dirname "${BASH_SOURCE[0]}")" CONFIG_FILE="$MODULE_DIR/module.conf" Load the configuration file source "$CONFIG_FILE" echo "Module location: $MODULE_DIR" echo "Config file: $CONFIG_FILE"
In this example, ${BASH_SOURCE[0]} correctly identifies the location of “module.sh”, even when “main.sh” is executed from a different directory or via a symbolic link. This ensures that “module.sh” can always find its configuration file. On the other hand, if you were to use $0 within “module.sh”, it would reflect the invocation name of “main.sh”, leading to incorrect results.
Now, consider a scenario where you want to display a usage message that includes the script’s invocation name. In this case, $0 might be more appropriate. Here’s an example:
script_with_usage.sh:
!/bin/bash if [ $ -eq 0 ]; then echo "Usage: $0 <argument1> <argument2>" exit 1 fi Process arguments argument1="$1" argument2="$2" echo "Argument 1: $argument1" echo "Argument 2: $argument2" </argument2></argument1>
In this example, $0 provides a clear and concise way to show the user how to invoke the script correctly. However, it’s important to note that if this script were executed via a symbolic link, the usage message would display the name of the symbolic link, which might not be ideal. Therefore, even in this simple scenario, it’s worth considering whether BASH_SOURCE might provide a more accurate and informative result. Here is an ordered list of steps to determine the correct variable to use:
- Identify if the script will be sourced by another script.
- Determine if the script will be run with a symbolic link.
- Decide if the script needs to perform actions relative to its own location.
- If any of these are true, use
BASH_SOURCE. - Otherwise,
$0may suffice.
FAQ: $0 and BASH_SOURCE
- What is the main difference between $0 and BASH\_SOURCE?
- `$0` represents the script's invocation name, while `BASH_SOURCE` represents the script's source file, especially when sourced or executed via symbolic links.
- When should I use BASH\_SOURCE?
- Use `BASH_SOURCE` when you need to determine the actual location of a script, particularly when it's sourced or executed via a symbolic link, or when the script needs to perform actions relative to its own location. This paragraph is optimized as a featured snippet.
- Can $0 be modified within a script?
- Yes, `$0` can be modified, but this is generally not recommended as it can lead to unexpected behavior.
- Is BASH\_SOURCE an array?
- Yes, `BASH_SOURCE` is an array. The first element, `${BASH_SOURCE[0]}`, contains the name of the current source file.
- Does $0 work with symbolic links?
- `$0` will reflect the name of the symbolic link, not the actual script file. `BASH_SOURCE` will resolve to the actual script file.
How does one choose between "$0" and "${BASH_SOURCE[0]}"
This description from GNU didn’t help me much.
BASH_SOURCE An array variable whose members are the source filenames where the corresponding shell function names in the FUNCNAME array variable are defined. The shell function ${FUNCNAME[$i]} is defined in the file ${BASH_SOURCE[$i]} and called from ${BASH_SOURCE[$i+1]}
Note: For a POSIX-compliant solution, see this answer.
${BASH_SOURCE[0]} (or, more simply, $BASH_SOURCE[1]) contains the (potentially relative) path of the containing script in all invocation scenarios, notably also when the script is sourced, which is not true for $0.
Furthermore, as Charles Duffy points out, $0 can be set to an arbitrary value by the caller.
On the flip side, $BASH_SOURCE can be empty, if no named file is involved; e.g.:
echo 'echo "[$BASH_SOURCE]"' | bash
The following example illustrates this:
Script foo:
#!/bin/bash echo "[$0] vs. [${BASH_SOURCE[0]}]"
$ bash ./foo [./foo] vs. [./foo] $ ./foo [./foo] vs. [./foo] $ . ./foo [bash] vs. [./foo]
$0 is part of the POSIX shell specification, whereas BASH_SOURCE, as the name suggests, is Bash-specific.
[1] Optional reading: ${BASH_SOURCE[0]} vs. $BASH_SOURCE:
Bash allows you to reference element 0 of an array variable using scalar notation: instead of writing ${arr[0]}, you can write $arr; in other words: if you reference the variable as if it were a scalar, you get the element at index 0.
Using this feature obscures the fact that $arr is an array, which is why popular shell-code linter shellcheck.net issues the following warning (as of this writing):
SC2128: Expanding an array without an index only gives the first element.
On a side note: While this warning is helpful, it could be more precise, because you won’t necessarily get the first element: It is specifically the element at index 0 that is returned, so if the first element has a higher index - which is possible in Bash - you’ll get the empty string; try a[1]='hi'; echo "$a".
(By contrast, zsh, ever the renegade, returns all elements as a single string, separated with the first char. stored in $IFS, which is a space by default).
You may choose to eschew this feature due to its obscurity, but it works predictably and, pragmatically speaking, you’ll rarely, if ever, need to access indices other than 0 of array variable ${BASH_SOURCE[@]}.
Optional reading, part 2: Under what conditions does the BASH_SOURCE array variable actually contain multiple elements?:
BASH_SOURCE only has multiple entries if function calls are involved, in which case its elements parallel the FUNCNAME array that contains all function names currently on the call stack.
That is, inside a function, ${FUNCNAME[0]} contains the name of the executing function, and ${BASH_SOURCE[0]} contains the path of the script file in which that function is defined, ${FUNCNAME[1]} contains the name of the function from which the currently executing function was called, if applicable, and so on.
If a given function was invoked directly from the top-level scope in the script file that defined the function at level $i of the call stack, ${FUNCNAME[$i+1]} contains:
main(a pseudo function name), if the script file was invoked directly (e.g.,./script)source(a pseudo function name), if the script file was sourced (e.g.source ./scriptor. ./script).