1. 10
  1.  

  2. 1

    This is a nice technique, thanks for posting it! To understand a bit more as to why this works you can modify the script to show the value of $BASH_SOURCE and the $FUNCNAME arrays as well as $0 both during execution as a standalone script and when sourced from another script.

    #!/bin/bash
    
    function hello {
        declare -p BASH_SOURCE
        declare -p FUNCNAME
        printf '$0=%s\n' "$0"
        if [[ -n "$1" ]]; then
            name="$1"
        else
            name="World"
        fi
        echo "Hello, $name!"
    }
    declare -p BASH_SOURCE
    declare -p FUNCNAME
    printf '$0=%s\n' "$0"
    [[ "${BASH_SOURCE[0]}" == "$0" ]] && hello "$@"
    

    $BASH_SOURCE is an array of filenames whose members correspond to the function call stack in $FUNCNAME. ${FUNCNAME[0]} is always the current function and ${FUNCNAME[n+1]} is the calling function of n, i.e. each function call name is pushed to the front of the $FUNCNAME array. When commands are executed outside of a function, Bash still populates ${BASH_SOURCE[0]} with the filename containing the commands and when inside a function Bash adds a fake function named “main” to $FUNCNAME in order to indicate who called the function. Consequently $0 in a script matches ${BASH_SOURCE[0]} when executed directly, but when sourced from another script ${BASH_SOURCE[0]} will still be ./hello.bash for example and $0 will be the script which sourced ./hello.bash which is ./test.sh in my example below.

    $ ./hello.bash
    declare -a BASH_SOURCE=([0]="./hello.bash")
    declare -a FUNCNAME
    $0=./hello.bash
    declare -a BASH_SOURCE=([0]="./hello.bash" [1]="./hello.bash")
    declare -a FUNCNAME=([0]="hello" [1]="main")
    $0=./hello.bash
    Hello, World!
    
    $ ./test.sh
    declare -a BASH_SOURCE=([0]="./hello.bash" [1]="./test.sh")
    declare -a FUNCNAME
    $0=./test.sh
    declare -a BASH_SOURCE=([0]="./hello.bash" [1]="./test.sh")
    declare -a FUNCNAME=([0]="hello" [1]="main")
    $0=./test.sh
    Hello, World!