determining path to sourced shell script












72















Is there a way for a sourced shell script to find out the path to itself? I'm mainly concerned with bash, though I have some coworkers who use tcsh.



I'm guessing I may not have a ton of luck here, since sourcing causes commands to be executed in the current shell, so $0 is still the current shell's invocation, not the sourced script. My best thought currently is to do source $script $script, so that the first positional parameter contains the necessary information. Anyone have a better way?



To be clear, I am sourcing the script, not running it:



source foo.bash









share|improve this question

























  • related question that has 4200+ upvotes: stackoverflow.com/q/59895/52074

    – Trevor Boyd Smith
    Jan 29 at 14:21
















72















Is there a way for a sourced shell script to find out the path to itself? I'm mainly concerned with bash, though I have some coworkers who use tcsh.



I'm guessing I may not have a ton of luck here, since sourcing causes commands to be executed in the current shell, so $0 is still the current shell's invocation, not the sourced script. My best thought currently is to do source $script $script, so that the first positional parameter contains the necessary information. Anyone have a better way?



To be clear, I am sourcing the script, not running it:



source foo.bash









share|improve this question

























  • related question that has 4200+ upvotes: stackoverflow.com/q/59895/52074

    – Trevor Boyd Smith
    Jan 29 at 14:21














72












72








72


26






Is there a way for a sourced shell script to find out the path to itself? I'm mainly concerned with bash, though I have some coworkers who use tcsh.



I'm guessing I may not have a ton of luck here, since sourcing causes commands to be executed in the current shell, so $0 is still the current shell's invocation, not the sourced script. My best thought currently is to do source $script $script, so that the first positional parameter contains the necessary information. Anyone have a better way?



To be clear, I am sourcing the script, not running it:



source foo.bash









share|improve this question
















Is there a way for a sourced shell script to find out the path to itself? I'm mainly concerned with bash, though I have some coworkers who use tcsh.



I'm guessing I may not have a ton of luck here, since sourcing causes commands to be executed in the current shell, so $0 is still the current shell's invocation, not the sourced script. My best thought currently is to do source $script $script, so that the first positional parameter contains the necessary information. Anyone have a better way?



To be clear, I am sourcing the script, not running it:



source foo.bash






shell source






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 8 '10 at 16:32







Cascabel

















asked Dec 8 '10 at 15:02









CascabelCascabel

7161714




7161714













  • related question that has 4200+ upvotes: stackoverflow.com/q/59895/52074

    – Trevor Boyd Smith
    Jan 29 at 14:21



















  • related question that has 4200+ upvotes: stackoverflow.com/q/59895/52074

    – Trevor Boyd Smith
    Jan 29 at 14:21

















related question that has 4200+ upvotes: stackoverflow.com/q/59895/52074

– Trevor Boyd Smith
Jan 29 at 14:21





related question that has 4200+ upvotes: stackoverflow.com/q/59895/52074

– Trevor Boyd Smith
Jan 29 at 14:21










10 Answers
10






active

oldest

votes


















61














In tcsh, $_ at the beginning of the script will contain the location if the file was sourced and $0 contains it if it was run.



#!/bin/tcsh
set sourced=($_)
if ("$sourced" != "") then
echo "sourced $sourced[2]"
endif
if ("$0" != "tcsh") then
echo "run $0"
endif


In Bash:



#!/bin/bash
[[ $0 != $BASH_SOURCE ]] && echo "Script is being sourced" || echo "Script is being run"





share|improve this answer


























  • I just had occasion to use this in tcsh, and noticed that it doesn't work without the shebang. Seems a bit odd for the behavior to change if you're just sourcing it, not executing it...

    – Cascabel
    Apr 20 '11 at 16:28











  • The tcsh version also doesn't seem to work if the script is sourced noninteractively (e.g. from a cshrc). I can't seem to find a way to get the information in that case. Any thoughts?

    – Cascabel
    May 9 '11 at 18:04











  • Sourcing it works for me without the shebang. > tcsh --versionn tcsh 6.14.00 (Astron) 2005-03-25 (i486-intel-linux) options wide,nls,dl,al,kan,rh,nd,color,filec. As far as sourcing it non-interactively, the source file is included into the parent file as if it were actually a part of it (indistinguishably so) as you mention in your original question. I think your positional parameter workaround is probably the best approach. However, the usual question is "why do you want to do that" and the usual answer to the reply is "don't do that - do this instead" where "this" is often to store...

    – Dennis Williamson
    May 10 '11 at 15:33






  • 1





    @clacke: I find that in all the versions of Bash that I tested from 2.05b to 4.2.37, including 4.1.9, that . and source worked identically in this regard. Note that $_ must be accessed in the first statement in the file, otherwise it will contain the last argument of the previous command. I like to include the shebang for my own reference so I know what shell it's supposed to be for and for the editor so it uses syntax highlighting.

    – Dennis Williamson
    Mar 4 '13 at 11:51






  • 1





    Haha. Obviously I was testing by first doing source, then doing .. I apologize for being incompetent. They are indeed identical. Anyway, $BASH_SOURCE works.

    – clacke
    Mar 5 '13 at 7:35





















27














I think that you could use $BASH_SOURCE variable. It returns path that was executed:



pbm@tauri ~ $ /home/pbm/a.sh 
/home/pbm/a.sh
pbm@tauri ~ $ ./a.sh
./a.sh
pbm@tauri ~ $ source /home/pbm/a.sh
/home/pbm/a.sh
pbm@tauri ~ $ source ./a.sh
./a.sh


So in next step we should check if path is relative or not. If it's not relative everything is ok. If it is we could check path with pwd, concatenate with / and $BASH_SOURCE.






share|improve this answer



















  • 2





    And note that source searches in $PATH if the given name doesn't contain a /. The search order depends on shell options, refer to the manual for details.

    – Gilles
    Dec 8 '10 at 19:09






  • 1





    So, something like mydir="$(cd "$(dirname "$BASH_SOURCE")"; pwd)" would work?

    – Kevin Cantu
    Dec 8 '10 at 19:36











  • Thanks, a quick and helpful answer. Dennis wins the green check mark for giving a tcsh answer too. @Gilles: Right, I did find that in the documentation. Fortunately for my use case I almost certainly don't have to worry about it.

    – Cascabel
    Dec 8 '10 at 19:46



















16














For thoroughness and the sake of searchers, here is what these do...
It is a community wiki, so feel free to add other shell's equivalents (obviously, $BASH_SOURCE will be different).



test.sh:



#! /bin/sh
called=$_
echo $called
echo $_
echo $0
echo $BASH_SOURCE


test2.sh:



#! /bin/sh
source ./test.sh


Bash:



$./test2.sh
./test2.sh
./test2.sh
./test2.sh
./test.sh
$ sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh
./test.sh


Dash



$./test2.sh
./test2.sh
./test2.sh
./test2.sh

$/bin/sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh

$


Zsh



$ ./test2.sh
./test.sh
./test.sh
./test.sh

$ zsh test.sh

echo
test.sh

$





share|improve this answer





















  • 1





    I don't understand: why called=$_; echo $called; echo $_? Won't this print $_ twice?

    – Ciro Santilli 新疆改造中心 六四事件 法轮功
    Sep 14 '14 at 7:17








  • 5





    @CiroSantilli: Not always, read the Bash manual on the $_ special parameter: "At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. Subsequently, expands to the last argument to the previous command, after expansion. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file."

    – Adam Rosenfield
    Sep 17 '14 at 19:14













  • Problem with this is the sourced file has header #! /bin/sh which makes it useless to source. That would start a new instance of /bin/sh, set variables, then exit that instance, leaving the calling instance unchanged.

    – JamesThomasMoon1979
    yesterday



















14














This solution applies only to bash and not tcsh. Note that the commonly supplied answer ${BASH_SOURCE[0]} won't work if you try to find the path from within a function.



I've found this line to always work, regardless of whether the file is being sourced or run as a script.



echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}


If you want to follow symlinks use readlink on the path you get above, recursively or non-recursively.



Here's a script to try it out and compare it to other proposed solutions. Invoke it as source test1/test2/test_script.sh or bash test1/test2/test_script.sh.



#
# Location: test1/test2/test_script.sh
#
echo $0
echo $_
echo ${BASH_SOURCE}
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}

cur_file="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
cur_dir="$(dirname "${cur_file}")"
source "${cur_dir}/func_def.sh"

function test_within_func_inside {
echo ${BASH_SOURCE}
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}

echo "Testing within function inside"
test_within_func_inside

echo "Testing within function outside"
test_within_func_outside

#
# Location: test1/test2/func_def.sh
#
function test_within_func_outside {
echo ${BASH_SOURCE}
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}


The reason the one-liner works is explained by the use of the BASH_SOURCE environment variable and its associate FUNCNAME.




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



FUNCNAME



An array variable containing the names of all shell functions currently in the execution call stack. The element with index 0 is the name of any currently-executing shell function. The bottom-most element (the one with the highest index) is "main". This variable exists only when a shell function is executing. Assignments to FUNCNAME have no effect and return an error status. If FUNCNAME is unset, it loses its special properties, even if it is subsequently reset.



This variable can be used with BASH_LINENO and BASH_SOURCE. Each element of FUNCNAME has corresponding elements in BASH_LINENO and BASH_SOURCE to describe the call stack. For instance, ${FUNCNAME[$i]} was called from the file ${BASH_SOURCE[$i+1]} at line number ${BASH_LINENO[$i]}. The caller builtin displays the current call stack using this information.




[Source: Bash manual]






share|improve this answer


























  • Can't see why this was downvoted. The accepted answer doesn't let you find out what directory the script is in from within a function. But the proposed solution does. Knee-jerk downvotes don't help the community.

    – gkb0986
    Aug 31 '14 at 21:49











  • @mikeserv I wouldn't be so sure. The question says "I'm mainly concerned with bash, though I have some coworkers who use tcsh". It also includes the suggestive invocation source foo.bash. I've edited my answer to include at the top that this is a bash only solution.

    – gkb0986
    Aug 31 '14 at 22:00











  • This solution worked for me in bash while the selected answer worked only intermittently. I never did figure out why it worked sometimes and not others (maybe I wasn't paying close enough attention to the sourcing shell).

    – Jim2B
    Sep 9 '15 at 15:45



















10














This worked for me in bash, dash, ksh, and zsh:



if test -n "$BASH" ; then script=$BASH_SOURCE
elif test -n "$TMOUT"; then script=${.sh.file}
elif test -n "$ZSH_NAME" ; then script=${(%):-%x}
elif test ${0##*/} = dash; then x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}
else script=$0
fi

echo $script


Output for these shells:



BASH source: ./myscript
ZSH source: ./myscript
KSH source: /home/pbrannan/git/theme/src/theme/web/myscript
DASH source: /home/pbrannan/git/theme/src/theme/web/myscript
BASH: ./myscript
ZSH: ./myscript
KSH: /home/pbrannan/git/theme/src/theme/web/myscript
DASH: ./myscript


I tried to make it work for csh/tcsh, but it's too hard; I'm sticking to POSIX.






share|improve this answer































    1














    I was a bit confused by the community wiki answer (from Shawn J. Goff), so I wrote a script to sort things out. About $_, I found this: Usage of _ as an environment variable passed to a command. It's an environment variable so it's easy to test its value incorrectly.



    Below is the script, then it's output. They also are in this gist.



    test-shell-default-variables.sh





    #!/bin/bash

    # test-shell-default-variables.sh

    # Usage examples (you might want to `sudo apt install zsh ksh`):
    #
    # ./test-shell-default-variables.sh dash bash
    # ./test-shell-default-variables.sh dash bash zsh ksh
    # ./test-shell-default-variables.sh dash bash zsh ksh | less -R

    # `-R` in `less -R` to have less pass escape sequences directly to the terminal
    # so we have colors.


    # The "invoking with name `sh`" tests are commented because for every shell I
    # tested (dash, bash, zsh and ksh), the output was the same as that of dash.

    # The `test_expression` function also work with expansion changes. You can try
    # lines like `test_expression '{BASH_SOURCE:-$0}'`.

    echolor() {
    echo -e "e[1;36m$@e[0m"
    }

    tell_file() {
    echo File `"$1"` is:
    echo ```
    cat "$1"
    echo ```
    echo
    }

    SHELL_ARRAY=("$@")

    test_command() {
    for shell in "${SHELL_ARRAY[@]}"
    do
    prepare "$shell"
    cmd="$(eval echo $1)"
    # echo "cmd: $cmd"
    printf '%-4s: ' "$shell"
    { env -i $cmd 2>&1 1>&3 | sed 's/^/[err]/'; } 3>&1
    teardown
    done
    echo
    }

    prepare () {
    shell="$1"
    PATH="$PWD/$shell/sh:$PATH"
    }

    teardown() {
    PATH="${PATH#*:}"
    }


    ###
    ### prepare
    ###
    for shell in "${SHELL_ARRAY[@]}"
    do
    mkdir "$shell"
    ln -sT "/bin/$shell" "$shell/sh"
    done

    echo > printer.sh
    echo '. ./printer.sh' > sourcer.sh
    rm linked.sh &>/dev/null; ln -sT "printer.sh" "linked.sh"

    tell_file sourcer.sh

    ###
    ### run
    ###
    test_expression() {
    local expr="$1"

    # prepare
    echo "echo $expr" > printer.sh
    tell_file printer.sh

    # run
    cmd='$shell ./printer.sh'
    echolor "`$cmd` (simple invocation) ($expr):"
    test_command "$cmd"

    # cmd='sh ./printer.sh'
    # echolor "`$cmd` (when executable name is `sh`) ($expr):"
    # test_command "$cmd"

    cmd='$shell ./sourcer.sh'
    echolor "`$cmd` (via sourcing) ($expr):"
    test_command "$cmd"

    # cmd='sh ./sourcer.sh'
    # echolor "`$cmd` (via sourcing, when name is `sh`) ($expr):"
    # test_command "$cmd"

    cmd='$shell ./linked.sh'
    echolor "`$cmd` (via symlink) ($expr):"
    test_command "$cmd"

    # cmd='sh ./linked.sh'
    # echolor "`$cmd` (via symlink, when name is `sh`) ($expr):"
    # test_command "$cmd"

    echolor "------------------------------------------"
    echo
    }

    test_expression '$BASH_SOURCE'
    test_expression '$0'
    test_expression '$(/bin/true x y; true a b c; echo $_)' # Rq: true is a builtin
    test_expression '$_'

    ###
    ### teardown
    ###
    for shell in "${SHELL_ARRAY[@]}"
    do
    rm "$shell/sh"
    rm -d "$shell"
    done

    rm sourcer.sh
    rm linked.sh
    rm printer.sh


    Output of ./test-shell-default-variables.sh {da,ba,z,k}sh





    File `sourcer.sh` is:
    ```
    . ./printer.sh
    ```

    File `printer.sh` is:
    ```
    echo $BASH_SOURCE
    ```

    `$shell ./printer.sh` (simple invocation) ($BASH_SOURCE):
    dash:
    bash: ./printer.sh
    zsh :
    ksh :

    `$shell ./sourcer.sh` (via sourcing) ($BASH_SOURCE):
    dash:
    bash: ./printer.sh
    zsh :
    ksh :

    `$shell ./linked.sh` (via symlink) ($BASH_SOURCE):
    dash:
    bash: ./linked.sh
    zsh :
    ksh :

    ------------------------------------------

    File `printer.sh` is:
    ```
    echo $0
    ```

    `$shell ./printer.sh` (simple invocation) ($0):
    dash: ./printer.sh
    bash: ./printer.sh
    zsh : ./printer.sh
    ksh : ./printer.sh

    `$shell ./sourcer.sh` (via sourcing) ($0):
    dash: ./sourcer.sh
    bash: ./sourcer.sh
    zsh : ./printer.sh
    ksh : ./sourcer.sh

    `$shell ./linked.sh` (via symlink) ($0):
    dash: ./linked.sh
    bash: ./linked.sh
    zsh : ./linked.sh
    ksh : ./linked.sh

    ------------------------------------------

    File `printer.sh` is:
    ```
    echo $(/bin/true x y; true a b c; echo $_)
    ```

    `$shell ./printer.sh` (simple invocation) ($(/bin/true x y; true a b c; echo $_)):
    dash:
    bash: c
    zsh : c
    ksh :

    `$shell ./sourcer.sh` (via sourcing) ($(/bin/true x y; true a b c; echo $_)):
    dash:
    bash: c
    zsh : c
    ksh :

    `$shell ./linked.sh` (via symlink) ($(/bin/true x y; true a b c; echo $_)):
    dash:
    bash: c
    zsh : c
    ksh :

    ------------------------------------------

    File `printer.sh` is:
    ```
    echo $_
    ```

    `$shell ./printer.sh` (simple invocation) ($_):
    dash:
    bash: bash
    zsh :
    ksh :

    `$shell ./sourcer.sh` (via sourcing) ($_):
    dash:
    bash: bash
    zsh : ./printer.sh
    ksh :

    `$shell ./linked.sh` (via symlink) ($_):
    dash:
    bash: bash
    zsh :
    ksh :

    ------------------------------------------


    What did we learn ?



    $BASH_SOURCE





    • $BASH_SOURCE works in bash and only in bash.

    • The only difference with $0 is when the current file was sourced by
      another file. In that case, $BASH_PROFILE contains the name of the
      sourced file, rather than that of the souring file.


    $0




    • In zsh, $0 has the same value as $BASH_SOURCE in bash.


    $_





    • $_ is left untouched by dash and ksh.

    • In bash and zsh, $_ decays to the last argument of the last call.

    • bash initializes $_ to "bash".

    • zsh leaves $_ untouched.
      (when sourcing, it`s just the result of the "last argument" rule).


    Symlinks




    • When a script is called through a symlink, no variable contains any
      reference to the destination of the link, only its name.


    ksh




    • Regarding those tests, ksh behaves like dash.


    sh




    • When bash or zsh is called through a symlink named sh, regarding those tests, it behave like dash.






    share|improve this answer































      0














      For the bash shell, I found @Dennis Williamson's answer most helpful, but it didn't work in the case of sudo. This does:



      if ( [[ $_ != $0 ]] && [[ $_ != $SHELL ]] ); then
      echo "I'm being sourced!"
      exit 1
      fi





      share|improve this answer

































        0














        To make your script both bash- and zsh-compatible instead of using if statements you can simply write ${BASH_SOURCE[0]:-${(%):-%x}}. The resulting value will be taken from BASH_SOURCE[0] when it's defined, and ${(%):-%x}} when BASH_SOURCE[0] is not defined.






        share|improve this answer































          0














          tl;dr script=$(readlink -e -- "${BASH_SOURCE}") (for bash obviously)







          $BASH_SOURCE test cases



          given file /tmp/source1.sh



          echo '$BASH_SOURCE '"(${BASH_SOURCE})"
          echo 'readlink -e $BASH_SOURCE'
          "($(readlink -e -- "${BASH_SOURCE}"))"


          source the file in different manners




          source from /tmp



          $> cd /tmp

          $> source source1.sh
          $BASH_SOURCE (source1.sh)
          readlink -e $BASH_SOURCE (/tmp/source1.sh)

          $> source ./source1.sh
          $BASH_SOURCE (./source1.sh)
          readlink -e $BASH_SOURCE (/tmp/source1.sh)

          $> source /tmp/source1.sh
          $BASH_SOURCE (/tmp/source1.sh)
          readlink -e $BASH_SOURCE (/tmp/source1.sh)




          source from /



          cd /
          $> source /tmp/source1.sh
          $0 (bash)
          $BASH_SOURCE (/tmp/source1.sh)
          readlink -e $BASH_SOURCE (/tmp/source1.sh)



          source from different relative paths /tmp/a and /var



          $> cd /tmp/a

          $> source ../source1.sh
          $BASH_SOURCE (../source1.sh)
          readlink -e $BASH_SOURCE (/tmp/source1.sh)

          $> cd /var

          $> source ../tmp/source1.sh
          $BASH_SOURCE (../tmp/source1.sh)
          readlink -e $BASH_SOURCE (/tmp/source1.sh)


          regarding $0



          in all cases, if the script had the added command



          echo '$0 '"(${0})"


          then source the script always printed



          $0 (bash)


          however, if the script was run, e.g.



          $> bash /tmp/source1.sh


          then $0 would be string value /tmp/source1.sh.



          $0 (/tmp/source1.sh)
          $BASH_SOURCE (/tmp/source1.sh)
          readlink -e $BASH_SOURCE (/tmp/source1.sh)





          share|improve this answer

































            -2














            Actually, "dirname $0" will get you the path to the script, but you have to interpret it a bit:



            $ cat bash0
            #!/bin/bash
            echo $0=$0
            dirname $0
            $ bash0 # "." appears in PATH right now.
            $0=./bash0
            .
            $ ./bash0
            $0=./bash0
            .
            $ $PWD/bash0
            $0=/home/00/bediger/src/ksh/bash0
            /home/00/bediger/src/ksh
            $ $PWD/../ksh/bash0
            $0=/home/00/bediger/src/ksh/../ksh/bash0
            /home/00/bediger/src/ksh/../ksh
            $ ../ksh/bash0
            $0=../ksh/bash0
            ../ksh


            You have to prepare to handle "." as the directory name under some common circumstances. I'd experiment a bit, as I remember the dirname built-in to ksh doing things a bit differently when "." appears in PATH.






            share|improve this answer



















            • 4





              This is a sourced script, not an executed script. $0 simply contains "bash" for an interactive shell, and that's all the sourced script sees.

              – Cascabel
              Dec 8 '10 at 16:32













            Your Answer








            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "106"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f4650%2fdetermining-path-to-sourced-shell-script%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            10 Answers
            10






            active

            oldest

            votes








            10 Answers
            10






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            61














            In tcsh, $_ at the beginning of the script will contain the location if the file was sourced and $0 contains it if it was run.



            #!/bin/tcsh
            set sourced=($_)
            if ("$sourced" != "") then
            echo "sourced $sourced[2]"
            endif
            if ("$0" != "tcsh") then
            echo "run $0"
            endif


            In Bash:



            #!/bin/bash
            [[ $0 != $BASH_SOURCE ]] && echo "Script is being sourced" || echo "Script is being run"





            share|improve this answer


























            • I just had occasion to use this in tcsh, and noticed that it doesn't work without the shebang. Seems a bit odd for the behavior to change if you're just sourcing it, not executing it...

              – Cascabel
              Apr 20 '11 at 16:28











            • The tcsh version also doesn't seem to work if the script is sourced noninteractively (e.g. from a cshrc). I can't seem to find a way to get the information in that case. Any thoughts?

              – Cascabel
              May 9 '11 at 18:04











            • Sourcing it works for me without the shebang. > tcsh --versionn tcsh 6.14.00 (Astron) 2005-03-25 (i486-intel-linux) options wide,nls,dl,al,kan,rh,nd,color,filec. As far as sourcing it non-interactively, the source file is included into the parent file as if it were actually a part of it (indistinguishably so) as you mention in your original question. I think your positional parameter workaround is probably the best approach. However, the usual question is "why do you want to do that" and the usual answer to the reply is "don't do that - do this instead" where "this" is often to store...

              – Dennis Williamson
              May 10 '11 at 15:33






            • 1





              @clacke: I find that in all the versions of Bash that I tested from 2.05b to 4.2.37, including 4.1.9, that . and source worked identically in this regard. Note that $_ must be accessed in the first statement in the file, otherwise it will contain the last argument of the previous command. I like to include the shebang for my own reference so I know what shell it's supposed to be for and for the editor so it uses syntax highlighting.

              – Dennis Williamson
              Mar 4 '13 at 11:51






            • 1





              Haha. Obviously I was testing by first doing source, then doing .. I apologize for being incompetent. They are indeed identical. Anyway, $BASH_SOURCE works.

              – clacke
              Mar 5 '13 at 7:35


















            61














            In tcsh, $_ at the beginning of the script will contain the location if the file was sourced and $0 contains it if it was run.



            #!/bin/tcsh
            set sourced=($_)
            if ("$sourced" != "") then
            echo "sourced $sourced[2]"
            endif
            if ("$0" != "tcsh") then
            echo "run $0"
            endif


            In Bash:



            #!/bin/bash
            [[ $0 != $BASH_SOURCE ]] && echo "Script is being sourced" || echo "Script is being run"





            share|improve this answer


























            • I just had occasion to use this in tcsh, and noticed that it doesn't work without the shebang. Seems a bit odd for the behavior to change if you're just sourcing it, not executing it...

              – Cascabel
              Apr 20 '11 at 16:28











            • The tcsh version also doesn't seem to work if the script is sourced noninteractively (e.g. from a cshrc). I can't seem to find a way to get the information in that case. Any thoughts?

              – Cascabel
              May 9 '11 at 18:04











            • Sourcing it works for me without the shebang. > tcsh --versionn tcsh 6.14.00 (Astron) 2005-03-25 (i486-intel-linux) options wide,nls,dl,al,kan,rh,nd,color,filec. As far as sourcing it non-interactively, the source file is included into the parent file as if it were actually a part of it (indistinguishably so) as you mention in your original question. I think your positional parameter workaround is probably the best approach. However, the usual question is "why do you want to do that" and the usual answer to the reply is "don't do that - do this instead" where "this" is often to store...

              – Dennis Williamson
              May 10 '11 at 15:33






            • 1





              @clacke: I find that in all the versions of Bash that I tested from 2.05b to 4.2.37, including 4.1.9, that . and source worked identically in this regard. Note that $_ must be accessed in the first statement in the file, otherwise it will contain the last argument of the previous command. I like to include the shebang for my own reference so I know what shell it's supposed to be for and for the editor so it uses syntax highlighting.

              – Dennis Williamson
              Mar 4 '13 at 11:51






            • 1





              Haha. Obviously I was testing by first doing source, then doing .. I apologize for being incompetent. They are indeed identical. Anyway, $BASH_SOURCE works.

              – clacke
              Mar 5 '13 at 7:35
















            61












            61








            61







            In tcsh, $_ at the beginning of the script will contain the location if the file was sourced and $0 contains it if it was run.



            #!/bin/tcsh
            set sourced=($_)
            if ("$sourced" != "") then
            echo "sourced $sourced[2]"
            endif
            if ("$0" != "tcsh") then
            echo "run $0"
            endif


            In Bash:



            #!/bin/bash
            [[ $0 != $BASH_SOURCE ]] && echo "Script is being sourced" || echo "Script is being run"





            share|improve this answer















            In tcsh, $_ at the beginning of the script will contain the location if the file was sourced and $0 contains it if it was run.



            #!/bin/tcsh
            set sourced=($_)
            if ("$sourced" != "") then
            echo "sourced $sourced[2]"
            endif
            if ("$0" != "tcsh") then
            echo "run $0"
            endif


            In Bash:



            #!/bin/bash
            [[ $0 != $BASH_SOURCE ]] && echo "Script is being sourced" || echo "Script is being run"






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Aug 28 '18 at 0:27

























            answered Dec 8 '10 at 19:27









            Dennis WilliamsonDennis Williamson

            5,03112333




            5,03112333













            • I just had occasion to use this in tcsh, and noticed that it doesn't work without the shebang. Seems a bit odd for the behavior to change if you're just sourcing it, not executing it...

              – Cascabel
              Apr 20 '11 at 16:28











            • The tcsh version also doesn't seem to work if the script is sourced noninteractively (e.g. from a cshrc). I can't seem to find a way to get the information in that case. Any thoughts?

              – Cascabel
              May 9 '11 at 18:04











            • Sourcing it works for me without the shebang. > tcsh --versionn tcsh 6.14.00 (Astron) 2005-03-25 (i486-intel-linux) options wide,nls,dl,al,kan,rh,nd,color,filec. As far as sourcing it non-interactively, the source file is included into the parent file as if it were actually a part of it (indistinguishably so) as you mention in your original question. I think your positional parameter workaround is probably the best approach. However, the usual question is "why do you want to do that" and the usual answer to the reply is "don't do that - do this instead" where "this" is often to store...

              – Dennis Williamson
              May 10 '11 at 15:33






            • 1





              @clacke: I find that in all the versions of Bash that I tested from 2.05b to 4.2.37, including 4.1.9, that . and source worked identically in this regard. Note that $_ must be accessed in the first statement in the file, otherwise it will contain the last argument of the previous command. I like to include the shebang for my own reference so I know what shell it's supposed to be for and for the editor so it uses syntax highlighting.

              – Dennis Williamson
              Mar 4 '13 at 11:51






            • 1





              Haha. Obviously I was testing by first doing source, then doing .. I apologize for being incompetent. They are indeed identical. Anyway, $BASH_SOURCE works.

              – clacke
              Mar 5 '13 at 7:35





















            • I just had occasion to use this in tcsh, and noticed that it doesn't work without the shebang. Seems a bit odd for the behavior to change if you're just sourcing it, not executing it...

              – Cascabel
              Apr 20 '11 at 16:28











            • The tcsh version also doesn't seem to work if the script is sourced noninteractively (e.g. from a cshrc). I can't seem to find a way to get the information in that case. Any thoughts?

              – Cascabel
              May 9 '11 at 18:04











            • Sourcing it works for me without the shebang. > tcsh --versionn tcsh 6.14.00 (Astron) 2005-03-25 (i486-intel-linux) options wide,nls,dl,al,kan,rh,nd,color,filec. As far as sourcing it non-interactively, the source file is included into the parent file as if it were actually a part of it (indistinguishably so) as you mention in your original question. I think your positional parameter workaround is probably the best approach. However, the usual question is "why do you want to do that" and the usual answer to the reply is "don't do that - do this instead" where "this" is often to store...

              – Dennis Williamson
              May 10 '11 at 15:33






            • 1





              @clacke: I find that in all the versions of Bash that I tested from 2.05b to 4.2.37, including 4.1.9, that . and source worked identically in this regard. Note that $_ must be accessed in the first statement in the file, otherwise it will contain the last argument of the previous command. I like to include the shebang for my own reference so I know what shell it's supposed to be for and for the editor so it uses syntax highlighting.

              – Dennis Williamson
              Mar 4 '13 at 11:51






            • 1





              Haha. Obviously I was testing by first doing source, then doing .. I apologize for being incompetent. They are indeed identical. Anyway, $BASH_SOURCE works.

              – clacke
              Mar 5 '13 at 7:35



















            I just had occasion to use this in tcsh, and noticed that it doesn't work without the shebang. Seems a bit odd for the behavior to change if you're just sourcing it, not executing it...

            – Cascabel
            Apr 20 '11 at 16:28





            I just had occasion to use this in tcsh, and noticed that it doesn't work without the shebang. Seems a bit odd for the behavior to change if you're just sourcing it, not executing it...

            – Cascabel
            Apr 20 '11 at 16:28













            The tcsh version also doesn't seem to work if the script is sourced noninteractively (e.g. from a cshrc). I can't seem to find a way to get the information in that case. Any thoughts?

            – Cascabel
            May 9 '11 at 18:04





            The tcsh version also doesn't seem to work if the script is sourced noninteractively (e.g. from a cshrc). I can't seem to find a way to get the information in that case. Any thoughts?

            – Cascabel
            May 9 '11 at 18:04













            Sourcing it works for me without the shebang. > tcsh --versionn tcsh 6.14.00 (Astron) 2005-03-25 (i486-intel-linux) options wide,nls,dl,al,kan,rh,nd,color,filec. As far as sourcing it non-interactively, the source file is included into the parent file as if it were actually a part of it (indistinguishably so) as you mention in your original question. I think your positional parameter workaround is probably the best approach. However, the usual question is "why do you want to do that" and the usual answer to the reply is "don't do that - do this instead" where "this" is often to store...

            – Dennis Williamson
            May 10 '11 at 15:33





            Sourcing it works for me without the shebang. > tcsh --versionn tcsh 6.14.00 (Astron) 2005-03-25 (i486-intel-linux) options wide,nls,dl,al,kan,rh,nd,color,filec. As far as sourcing it non-interactively, the source file is included into the parent file as if it were actually a part of it (indistinguishably so) as you mention in your original question. I think your positional parameter workaround is probably the best approach. However, the usual question is "why do you want to do that" and the usual answer to the reply is "don't do that - do this instead" where "this" is often to store...

            – Dennis Williamson
            May 10 '11 at 15:33




            1




            1





            @clacke: I find that in all the versions of Bash that I tested from 2.05b to 4.2.37, including 4.1.9, that . and source worked identically in this regard. Note that $_ must be accessed in the first statement in the file, otherwise it will contain the last argument of the previous command. I like to include the shebang for my own reference so I know what shell it's supposed to be for and for the editor so it uses syntax highlighting.

            – Dennis Williamson
            Mar 4 '13 at 11:51





            @clacke: I find that in all the versions of Bash that I tested from 2.05b to 4.2.37, including 4.1.9, that . and source worked identically in this regard. Note that $_ must be accessed in the first statement in the file, otherwise it will contain the last argument of the previous command. I like to include the shebang for my own reference so I know what shell it's supposed to be for and for the editor so it uses syntax highlighting.

            – Dennis Williamson
            Mar 4 '13 at 11:51




            1




            1





            Haha. Obviously I was testing by first doing source, then doing .. I apologize for being incompetent. They are indeed identical. Anyway, $BASH_SOURCE works.

            – clacke
            Mar 5 '13 at 7:35







            Haha. Obviously I was testing by first doing source, then doing .. I apologize for being incompetent. They are indeed identical. Anyway, $BASH_SOURCE works.

            – clacke
            Mar 5 '13 at 7:35















            27














            I think that you could use $BASH_SOURCE variable. It returns path that was executed:



            pbm@tauri ~ $ /home/pbm/a.sh 
            /home/pbm/a.sh
            pbm@tauri ~ $ ./a.sh
            ./a.sh
            pbm@tauri ~ $ source /home/pbm/a.sh
            /home/pbm/a.sh
            pbm@tauri ~ $ source ./a.sh
            ./a.sh


            So in next step we should check if path is relative or not. If it's not relative everything is ok. If it is we could check path with pwd, concatenate with / and $BASH_SOURCE.






            share|improve this answer



















            • 2





              And note that source searches in $PATH if the given name doesn't contain a /. The search order depends on shell options, refer to the manual for details.

              – Gilles
              Dec 8 '10 at 19:09






            • 1





              So, something like mydir="$(cd "$(dirname "$BASH_SOURCE")"; pwd)" would work?

              – Kevin Cantu
              Dec 8 '10 at 19:36











            • Thanks, a quick and helpful answer. Dennis wins the green check mark for giving a tcsh answer too. @Gilles: Right, I did find that in the documentation. Fortunately for my use case I almost certainly don't have to worry about it.

              – Cascabel
              Dec 8 '10 at 19:46
















            27














            I think that you could use $BASH_SOURCE variable. It returns path that was executed:



            pbm@tauri ~ $ /home/pbm/a.sh 
            /home/pbm/a.sh
            pbm@tauri ~ $ ./a.sh
            ./a.sh
            pbm@tauri ~ $ source /home/pbm/a.sh
            /home/pbm/a.sh
            pbm@tauri ~ $ source ./a.sh
            ./a.sh


            So in next step we should check if path is relative or not. If it's not relative everything is ok. If it is we could check path with pwd, concatenate with / and $BASH_SOURCE.






            share|improve this answer



















            • 2





              And note that source searches in $PATH if the given name doesn't contain a /. The search order depends on shell options, refer to the manual for details.

              – Gilles
              Dec 8 '10 at 19:09






            • 1





              So, something like mydir="$(cd "$(dirname "$BASH_SOURCE")"; pwd)" would work?

              – Kevin Cantu
              Dec 8 '10 at 19:36











            • Thanks, a quick and helpful answer. Dennis wins the green check mark for giving a tcsh answer too. @Gilles: Right, I did find that in the documentation. Fortunately for my use case I almost certainly don't have to worry about it.

              – Cascabel
              Dec 8 '10 at 19:46














            27












            27








            27







            I think that you could use $BASH_SOURCE variable. It returns path that was executed:



            pbm@tauri ~ $ /home/pbm/a.sh 
            /home/pbm/a.sh
            pbm@tauri ~ $ ./a.sh
            ./a.sh
            pbm@tauri ~ $ source /home/pbm/a.sh
            /home/pbm/a.sh
            pbm@tauri ~ $ source ./a.sh
            ./a.sh


            So in next step we should check if path is relative or not. If it's not relative everything is ok. If it is we could check path with pwd, concatenate with / and $BASH_SOURCE.






            share|improve this answer













            I think that you could use $BASH_SOURCE variable. It returns path that was executed:



            pbm@tauri ~ $ /home/pbm/a.sh 
            /home/pbm/a.sh
            pbm@tauri ~ $ ./a.sh
            ./a.sh
            pbm@tauri ~ $ source /home/pbm/a.sh
            /home/pbm/a.sh
            pbm@tauri ~ $ source ./a.sh
            ./a.sh


            So in next step we should check if path is relative or not. If it's not relative everything is ok. If it is we could check path with pwd, concatenate with / and $BASH_SOURCE.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Dec 8 '10 at 15:52









            pbmpbm

            17.2k52847




            17.2k52847








            • 2





              And note that source searches in $PATH if the given name doesn't contain a /. The search order depends on shell options, refer to the manual for details.

              – Gilles
              Dec 8 '10 at 19:09






            • 1





              So, something like mydir="$(cd "$(dirname "$BASH_SOURCE")"; pwd)" would work?

              – Kevin Cantu
              Dec 8 '10 at 19:36











            • Thanks, a quick and helpful answer. Dennis wins the green check mark for giving a tcsh answer too. @Gilles: Right, I did find that in the documentation. Fortunately for my use case I almost certainly don't have to worry about it.

              – Cascabel
              Dec 8 '10 at 19:46














            • 2





              And note that source searches in $PATH if the given name doesn't contain a /. The search order depends on shell options, refer to the manual for details.

              – Gilles
              Dec 8 '10 at 19:09






            • 1





              So, something like mydir="$(cd "$(dirname "$BASH_SOURCE")"; pwd)" would work?

              – Kevin Cantu
              Dec 8 '10 at 19:36











            • Thanks, a quick and helpful answer. Dennis wins the green check mark for giving a tcsh answer too. @Gilles: Right, I did find that in the documentation. Fortunately for my use case I almost certainly don't have to worry about it.

              – Cascabel
              Dec 8 '10 at 19:46








            2




            2





            And note that source searches in $PATH if the given name doesn't contain a /. The search order depends on shell options, refer to the manual for details.

            – Gilles
            Dec 8 '10 at 19:09





            And note that source searches in $PATH if the given name doesn't contain a /. The search order depends on shell options, refer to the manual for details.

            – Gilles
            Dec 8 '10 at 19:09




            1




            1





            So, something like mydir="$(cd "$(dirname "$BASH_SOURCE")"; pwd)" would work?

            – Kevin Cantu
            Dec 8 '10 at 19:36





            So, something like mydir="$(cd "$(dirname "$BASH_SOURCE")"; pwd)" would work?

            – Kevin Cantu
            Dec 8 '10 at 19:36













            Thanks, a quick and helpful answer. Dennis wins the green check mark for giving a tcsh answer too. @Gilles: Right, I did find that in the documentation. Fortunately for my use case I almost certainly don't have to worry about it.

            – Cascabel
            Dec 8 '10 at 19:46





            Thanks, a quick and helpful answer. Dennis wins the green check mark for giving a tcsh answer too. @Gilles: Right, I did find that in the documentation. Fortunately for my use case I almost certainly don't have to worry about it.

            – Cascabel
            Dec 8 '10 at 19:46











            16














            For thoroughness and the sake of searchers, here is what these do...
            It is a community wiki, so feel free to add other shell's equivalents (obviously, $BASH_SOURCE will be different).



            test.sh:



            #! /bin/sh
            called=$_
            echo $called
            echo $_
            echo $0
            echo $BASH_SOURCE


            test2.sh:



            #! /bin/sh
            source ./test.sh


            Bash:



            $./test2.sh
            ./test2.sh
            ./test2.sh
            ./test2.sh
            ./test.sh
            $ sh ./test2.sh
            /bin/sh
            /bin/sh
            ./test2.sh
            ./test.sh


            Dash



            $./test2.sh
            ./test2.sh
            ./test2.sh
            ./test2.sh

            $/bin/sh ./test2.sh
            /bin/sh
            /bin/sh
            ./test2.sh

            $


            Zsh



            $ ./test2.sh
            ./test.sh
            ./test.sh
            ./test.sh

            $ zsh test.sh

            echo
            test.sh

            $





            share|improve this answer





















            • 1





              I don't understand: why called=$_; echo $called; echo $_? Won't this print $_ twice?

              – Ciro Santilli 新疆改造中心 六四事件 法轮功
              Sep 14 '14 at 7:17








            • 5





              @CiroSantilli: Not always, read the Bash manual on the $_ special parameter: "At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. Subsequently, expands to the last argument to the previous command, after expansion. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file."

              – Adam Rosenfield
              Sep 17 '14 at 19:14













            • Problem with this is the sourced file has header #! /bin/sh which makes it useless to source. That would start a new instance of /bin/sh, set variables, then exit that instance, leaving the calling instance unchanged.

              – JamesThomasMoon1979
              yesterday
















            16














            For thoroughness and the sake of searchers, here is what these do...
            It is a community wiki, so feel free to add other shell's equivalents (obviously, $BASH_SOURCE will be different).



            test.sh:



            #! /bin/sh
            called=$_
            echo $called
            echo $_
            echo $0
            echo $BASH_SOURCE


            test2.sh:



            #! /bin/sh
            source ./test.sh


            Bash:



            $./test2.sh
            ./test2.sh
            ./test2.sh
            ./test2.sh
            ./test.sh
            $ sh ./test2.sh
            /bin/sh
            /bin/sh
            ./test2.sh
            ./test.sh


            Dash



            $./test2.sh
            ./test2.sh
            ./test2.sh
            ./test2.sh

            $/bin/sh ./test2.sh
            /bin/sh
            /bin/sh
            ./test2.sh

            $


            Zsh



            $ ./test2.sh
            ./test.sh
            ./test.sh
            ./test.sh

            $ zsh test.sh

            echo
            test.sh

            $





            share|improve this answer





















            • 1





              I don't understand: why called=$_; echo $called; echo $_? Won't this print $_ twice?

              – Ciro Santilli 新疆改造中心 六四事件 法轮功
              Sep 14 '14 at 7:17








            • 5





              @CiroSantilli: Not always, read the Bash manual on the $_ special parameter: "At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. Subsequently, expands to the last argument to the previous command, after expansion. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file."

              – Adam Rosenfield
              Sep 17 '14 at 19:14













            • Problem with this is the sourced file has header #! /bin/sh which makes it useless to source. That would start a new instance of /bin/sh, set variables, then exit that instance, leaving the calling instance unchanged.

              – JamesThomasMoon1979
              yesterday














            16












            16








            16







            For thoroughness and the sake of searchers, here is what these do...
            It is a community wiki, so feel free to add other shell's equivalents (obviously, $BASH_SOURCE will be different).



            test.sh:



            #! /bin/sh
            called=$_
            echo $called
            echo $_
            echo $0
            echo $BASH_SOURCE


            test2.sh:



            #! /bin/sh
            source ./test.sh


            Bash:



            $./test2.sh
            ./test2.sh
            ./test2.sh
            ./test2.sh
            ./test.sh
            $ sh ./test2.sh
            /bin/sh
            /bin/sh
            ./test2.sh
            ./test.sh


            Dash



            $./test2.sh
            ./test2.sh
            ./test2.sh
            ./test2.sh

            $/bin/sh ./test2.sh
            /bin/sh
            /bin/sh
            ./test2.sh

            $


            Zsh



            $ ./test2.sh
            ./test.sh
            ./test.sh
            ./test.sh

            $ zsh test.sh

            echo
            test.sh

            $





            share|improve this answer















            For thoroughness and the sake of searchers, here is what these do...
            It is a community wiki, so feel free to add other shell's equivalents (obviously, $BASH_SOURCE will be different).



            test.sh:



            #! /bin/sh
            called=$_
            echo $called
            echo $_
            echo $0
            echo $BASH_SOURCE


            test2.sh:



            #! /bin/sh
            source ./test.sh


            Bash:



            $./test2.sh
            ./test2.sh
            ./test2.sh
            ./test2.sh
            ./test.sh
            $ sh ./test2.sh
            /bin/sh
            /bin/sh
            ./test2.sh
            ./test.sh


            Dash



            $./test2.sh
            ./test2.sh
            ./test2.sh
            ./test2.sh

            $/bin/sh ./test2.sh
            /bin/sh
            /bin/sh
            ./test2.sh

            $


            Zsh



            $ ./test2.sh
            ./test.sh
            ./test.sh
            ./test.sh

            $ zsh test.sh

            echo
            test.sh

            $






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 6 '18 at 17:49


























            community wiki





            5 revs, 3 users 81%
            Shawn J. Goff









            • 1





              I don't understand: why called=$_; echo $called; echo $_? Won't this print $_ twice?

              – Ciro Santilli 新疆改造中心 六四事件 法轮功
              Sep 14 '14 at 7:17








            • 5





              @CiroSantilli: Not always, read the Bash manual on the $_ special parameter: "At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. Subsequently, expands to the last argument to the previous command, after expansion. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file."

              – Adam Rosenfield
              Sep 17 '14 at 19:14













            • Problem with this is the sourced file has header #! /bin/sh which makes it useless to source. That would start a new instance of /bin/sh, set variables, then exit that instance, leaving the calling instance unchanged.

              – JamesThomasMoon1979
              yesterday














            • 1





              I don't understand: why called=$_; echo $called; echo $_? Won't this print $_ twice?

              – Ciro Santilli 新疆改造中心 六四事件 法轮功
              Sep 14 '14 at 7:17








            • 5





              @CiroSantilli: Not always, read the Bash manual on the $_ special parameter: "At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. Subsequently, expands to the last argument to the previous command, after expansion. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file."

              – Adam Rosenfield
              Sep 17 '14 at 19:14













            • Problem with this is the sourced file has header #! /bin/sh which makes it useless to source. That would start a new instance of /bin/sh, set variables, then exit that instance, leaving the calling instance unchanged.

              – JamesThomasMoon1979
              yesterday








            1




            1





            I don't understand: why called=$_; echo $called; echo $_? Won't this print $_ twice?

            – Ciro Santilli 新疆改造中心 六四事件 法轮功
            Sep 14 '14 at 7:17







            I don't understand: why called=$_; echo $called; echo $_? Won't this print $_ twice?

            – Ciro Santilli 新疆改造中心 六四事件 法轮功
            Sep 14 '14 at 7:17






            5




            5





            @CiroSantilli: Not always, read the Bash manual on the $_ special parameter: "At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. Subsequently, expands to the last argument to the previous command, after expansion. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file."

            – Adam Rosenfield
            Sep 17 '14 at 19:14







            @CiroSantilli: Not always, read the Bash manual on the $_ special parameter: "At shell startup, set to the absolute pathname used to invoke the shell or shell script being executed as passed in the environment or argument list. Subsequently, expands to the last argument to the previous command, after expansion. Also set to the full pathname used to invoke each command executed and placed in the environment exported to that command. When checking mail, this parameter holds the name of the mail file."

            – Adam Rosenfield
            Sep 17 '14 at 19:14















            Problem with this is the sourced file has header #! /bin/sh which makes it useless to source. That would start a new instance of /bin/sh, set variables, then exit that instance, leaving the calling instance unchanged.

            – JamesThomasMoon1979
            yesterday





            Problem with this is the sourced file has header #! /bin/sh which makes it useless to source. That would start a new instance of /bin/sh, set variables, then exit that instance, leaving the calling instance unchanged.

            – JamesThomasMoon1979
            yesterday











            14














            This solution applies only to bash and not tcsh. Note that the commonly supplied answer ${BASH_SOURCE[0]} won't work if you try to find the path from within a function.



            I've found this line to always work, regardless of whether the file is being sourced or run as a script.



            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}


            If you want to follow symlinks use readlink on the path you get above, recursively or non-recursively.



            Here's a script to try it out and compare it to other proposed solutions. Invoke it as source test1/test2/test_script.sh or bash test1/test2/test_script.sh.



            #
            # Location: test1/test2/test_script.sh
            #
            echo $0
            echo $_
            echo ${BASH_SOURCE}
            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}

            cur_file="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
            cur_dir="$(dirname "${cur_file}")"
            source "${cur_dir}/func_def.sh"

            function test_within_func_inside {
            echo ${BASH_SOURCE}
            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
            }

            echo "Testing within function inside"
            test_within_func_inside

            echo "Testing within function outside"
            test_within_func_outside

            #
            # Location: test1/test2/func_def.sh
            #
            function test_within_func_outside {
            echo ${BASH_SOURCE}
            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
            }


            The reason the one-liner works is explained by the use of the BASH_SOURCE environment variable and its associate FUNCNAME.




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



            FUNCNAME



            An array variable containing the names of all shell functions currently in the execution call stack. The element with index 0 is the name of any currently-executing shell function. The bottom-most element (the one with the highest index) is "main". This variable exists only when a shell function is executing. Assignments to FUNCNAME have no effect and return an error status. If FUNCNAME is unset, it loses its special properties, even if it is subsequently reset.



            This variable can be used with BASH_LINENO and BASH_SOURCE. Each element of FUNCNAME has corresponding elements in BASH_LINENO and BASH_SOURCE to describe the call stack. For instance, ${FUNCNAME[$i]} was called from the file ${BASH_SOURCE[$i+1]} at line number ${BASH_LINENO[$i]}. The caller builtin displays the current call stack using this information.




            [Source: Bash manual]






            share|improve this answer


























            • Can't see why this was downvoted. The accepted answer doesn't let you find out what directory the script is in from within a function. But the proposed solution does. Knee-jerk downvotes don't help the community.

              – gkb0986
              Aug 31 '14 at 21:49











            • @mikeserv I wouldn't be so sure. The question says "I'm mainly concerned with bash, though I have some coworkers who use tcsh". It also includes the suggestive invocation source foo.bash. I've edited my answer to include at the top that this is a bash only solution.

              – gkb0986
              Aug 31 '14 at 22:00











            • This solution worked for me in bash while the selected answer worked only intermittently. I never did figure out why it worked sometimes and not others (maybe I wasn't paying close enough attention to the sourcing shell).

              – Jim2B
              Sep 9 '15 at 15:45
















            14














            This solution applies only to bash and not tcsh. Note that the commonly supplied answer ${BASH_SOURCE[0]} won't work if you try to find the path from within a function.



            I've found this line to always work, regardless of whether the file is being sourced or run as a script.



            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}


            If you want to follow symlinks use readlink on the path you get above, recursively or non-recursively.



            Here's a script to try it out and compare it to other proposed solutions. Invoke it as source test1/test2/test_script.sh or bash test1/test2/test_script.sh.



            #
            # Location: test1/test2/test_script.sh
            #
            echo $0
            echo $_
            echo ${BASH_SOURCE}
            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}

            cur_file="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
            cur_dir="$(dirname "${cur_file}")"
            source "${cur_dir}/func_def.sh"

            function test_within_func_inside {
            echo ${BASH_SOURCE}
            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
            }

            echo "Testing within function inside"
            test_within_func_inside

            echo "Testing within function outside"
            test_within_func_outside

            #
            # Location: test1/test2/func_def.sh
            #
            function test_within_func_outside {
            echo ${BASH_SOURCE}
            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
            }


            The reason the one-liner works is explained by the use of the BASH_SOURCE environment variable and its associate FUNCNAME.




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



            FUNCNAME



            An array variable containing the names of all shell functions currently in the execution call stack. The element with index 0 is the name of any currently-executing shell function. The bottom-most element (the one with the highest index) is "main". This variable exists only when a shell function is executing. Assignments to FUNCNAME have no effect and return an error status. If FUNCNAME is unset, it loses its special properties, even if it is subsequently reset.



            This variable can be used with BASH_LINENO and BASH_SOURCE. Each element of FUNCNAME has corresponding elements in BASH_LINENO and BASH_SOURCE to describe the call stack. For instance, ${FUNCNAME[$i]} was called from the file ${BASH_SOURCE[$i+1]} at line number ${BASH_LINENO[$i]}. The caller builtin displays the current call stack using this information.




            [Source: Bash manual]






            share|improve this answer


























            • Can't see why this was downvoted. The accepted answer doesn't let you find out what directory the script is in from within a function. But the proposed solution does. Knee-jerk downvotes don't help the community.

              – gkb0986
              Aug 31 '14 at 21:49











            • @mikeserv I wouldn't be so sure. The question says "I'm mainly concerned with bash, though I have some coworkers who use tcsh". It also includes the suggestive invocation source foo.bash. I've edited my answer to include at the top that this is a bash only solution.

              – gkb0986
              Aug 31 '14 at 22:00











            • This solution worked for me in bash while the selected answer worked only intermittently. I never did figure out why it worked sometimes and not others (maybe I wasn't paying close enough attention to the sourcing shell).

              – Jim2B
              Sep 9 '15 at 15:45














            14












            14








            14







            This solution applies only to bash and not tcsh. Note that the commonly supplied answer ${BASH_SOURCE[0]} won't work if you try to find the path from within a function.



            I've found this line to always work, regardless of whether the file is being sourced or run as a script.



            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}


            If you want to follow symlinks use readlink on the path you get above, recursively or non-recursively.



            Here's a script to try it out and compare it to other proposed solutions. Invoke it as source test1/test2/test_script.sh or bash test1/test2/test_script.sh.



            #
            # Location: test1/test2/test_script.sh
            #
            echo $0
            echo $_
            echo ${BASH_SOURCE}
            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}

            cur_file="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
            cur_dir="$(dirname "${cur_file}")"
            source "${cur_dir}/func_def.sh"

            function test_within_func_inside {
            echo ${BASH_SOURCE}
            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
            }

            echo "Testing within function inside"
            test_within_func_inside

            echo "Testing within function outside"
            test_within_func_outside

            #
            # Location: test1/test2/func_def.sh
            #
            function test_within_func_outside {
            echo ${BASH_SOURCE}
            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
            }


            The reason the one-liner works is explained by the use of the BASH_SOURCE environment variable and its associate FUNCNAME.




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



            FUNCNAME



            An array variable containing the names of all shell functions currently in the execution call stack. The element with index 0 is the name of any currently-executing shell function. The bottom-most element (the one with the highest index) is "main". This variable exists only when a shell function is executing. Assignments to FUNCNAME have no effect and return an error status. If FUNCNAME is unset, it loses its special properties, even if it is subsequently reset.



            This variable can be used with BASH_LINENO and BASH_SOURCE. Each element of FUNCNAME has corresponding elements in BASH_LINENO and BASH_SOURCE to describe the call stack. For instance, ${FUNCNAME[$i]} was called from the file ${BASH_SOURCE[$i+1]} at line number ${BASH_LINENO[$i]}. The caller builtin displays the current call stack using this information.




            [Source: Bash manual]






            share|improve this answer















            This solution applies only to bash and not tcsh. Note that the commonly supplied answer ${BASH_SOURCE[0]} won't work if you try to find the path from within a function.



            I've found this line to always work, regardless of whether the file is being sourced or run as a script.



            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}


            If you want to follow symlinks use readlink on the path you get above, recursively or non-recursively.



            Here's a script to try it out and compare it to other proposed solutions. Invoke it as source test1/test2/test_script.sh or bash test1/test2/test_script.sh.



            #
            # Location: test1/test2/test_script.sh
            #
            echo $0
            echo $_
            echo ${BASH_SOURCE}
            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}

            cur_file="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
            cur_dir="$(dirname "${cur_file}")"
            source "${cur_dir}/func_def.sh"

            function test_within_func_inside {
            echo ${BASH_SOURCE}
            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
            }

            echo "Testing within function inside"
            test_within_func_inside

            echo "Testing within function outside"
            test_within_func_outside

            #
            # Location: test1/test2/func_def.sh
            #
            function test_within_func_outside {
            echo ${BASH_SOURCE}
            echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
            }


            The reason the one-liner works is explained by the use of the BASH_SOURCE environment variable and its associate FUNCNAME.




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



            FUNCNAME



            An array variable containing the names of all shell functions currently in the execution call stack. The element with index 0 is the name of any currently-executing shell function. The bottom-most element (the one with the highest index) is "main". This variable exists only when a shell function is executing. Assignments to FUNCNAME have no effect and return an error status. If FUNCNAME is unset, it loses its special properties, even if it is subsequently reset.



            This variable can be used with BASH_LINENO and BASH_SOURCE. Each element of FUNCNAME has corresponding elements in BASH_LINENO and BASH_SOURCE to describe the call stack. For instance, ${FUNCNAME[$i]} was called from the file ${BASH_SOURCE[$i+1]} at line number ${BASH_LINENO[$i]}. The caller builtin displays the current call stack using this information.




            [Source: Bash manual]







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Aug 31 '14 at 22:05

























            answered Aug 31 '14 at 20:58









            gkb0986gkb0986

            1,656296




            1,656296













            • Can't see why this was downvoted. The accepted answer doesn't let you find out what directory the script is in from within a function. But the proposed solution does. Knee-jerk downvotes don't help the community.

              – gkb0986
              Aug 31 '14 at 21:49











            • @mikeserv I wouldn't be so sure. The question says "I'm mainly concerned with bash, though I have some coworkers who use tcsh". It also includes the suggestive invocation source foo.bash. I've edited my answer to include at the top that this is a bash only solution.

              – gkb0986
              Aug 31 '14 at 22:00











            • This solution worked for me in bash while the selected answer worked only intermittently. I never did figure out why it worked sometimes and not others (maybe I wasn't paying close enough attention to the sourcing shell).

              – Jim2B
              Sep 9 '15 at 15:45



















            • Can't see why this was downvoted. The accepted answer doesn't let you find out what directory the script is in from within a function. But the proposed solution does. Knee-jerk downvotes don't help the community.

              – gkb0986
              Aug 31 '14 at 21:49











            • @mikeserv I wouldn't be so sure. The question says "I'm mainly concerned with bash, though I have some coworkers who use tcsh". It also includes the suggestive invocation source foo.bash. I've edited my answer to include at the top that this is a bash only solution.

              – gkb0986
              Aug 31 '14 at 22:00











            • This solution worked for me in bash while the selected answer worked only intermittently. I never did figure out why it worked sometimes and not others (maybe I wasn't paying close enough attention to the sourcing shell).

              – Jim2B
              Sep 9 '15 at 15:45

















            Can't see why this was downvoted. The accepted answer doesn't let you find out what directory the script is in from within a function. But the proposed solution does. Knee-jerk downvotes don't help the community.

            – gkb0986
            Aug 31 '14 at 21:49





            Can't see why this was downvoted. The accepted answer doesn't let you find out what directory the script is in from within a function. But the proposed solution does. Knee-jerk downvotes don't help the community.

            – gkb0986
            Aug 31 '14 at 21:49













            @mikeserv I wouldn't be so sure. The question says "I'm mainly concerned with bash, though I have some coworkers who use tcsh". It also includes the suggestive invocation source foo.bash. I've edited my answer to include at the top that this is a bash only solution.

            – gkb0986
            Aug 31 '14 at 22:00





            @mikeserv I wouldn't be so sure. The question says "I'm mainly concerned with bash, though I have some coworkers who use tcsh". It also includes the suggestive invocation source foo.bash. I've edited my answer to include at the top that this is a bash only solution.

            – gkb0986
            Aug 31 '14 at 22:00













            This solution worked for me in bash while the selected answer worked only intermittently. I never did figure out why it worked sometimes and not others (maybe I wasn't paying close enough attention to the sourcing shell).

            – Jim2B
            Sep 9 '15 at 15:45





            This solution worked for me in bash while the selected answer worked only intermittently. I never did figure out why it worked sometimes and not others (maybe I wasn't paying close enough attention to the sourcing shell).

            – Jim2B
            Sep 9 '15 at 15:45











            10














            This worked for me in bash, dash, ksh, and zsh:



            if test -n "$BASH" ; then script=$BASH_SOURCE
            elif test -n "$TMOUT"; then script=${.sh.file}
            elif test -n "$ZSH_NAME" ; then script=${(%):-%x}
            elif test ${0##*/} = dash; then x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}
            else script=$0
            fi

            echo $script


            Output for these shells:



            BASH source: ./myscript
            ZSH source: ./myscript
            KSH source: /home/pbrannan/git/theme/src/theme/web/myscript
            DASH source: /home/pbrannan/git/theme/src/theme/web/myscript
            BASH: ./myscript
            ZSH: ./myscript
            KSH: /home/pbrannan/git/theme/src/theme/web/myscript
            DASH: ./myscript


            I tried to make it work for csh/tcsh, but it's too hard; I'm sticking to POSIX.






            share|improve this answer




























              10














              This worked for me in bash, dash, ksh, and zsh:



              if test -n "$BASH" ; then script=$BASH_SOURCE
              elif test -n "$TMOUT"; then script=${.sh.file}
              elif test -n "$ZSH_NAME" ; then script=${(%):-%x}
              elif test ${0##*/} = dash; then x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}
              else script=$0
              fi

              echo $script


              Output for these shells:



              BASH source: ./myscript
              ZSH source: ./myscript
              KSH source: /home/pbrannan/git/theme/src/theme/web/myscript
              DASH source: /home/pbrannan/git/theme/src/theme/web/myscript
              BASH: ./myscript
              ZSH: ./myscript
              KSH: /home/pbrannan/git/theme/src/theme/web/myscript
              DASH: ./myscript


              I tried to make it work for csh/tcsh, but it's too hard; I'm sticking to POSIX.






              share|improve this answer


























                10












                10








                10







                This worked for me in bash, dash, ksh, and zsh:



                if test -n "$BASH" ; then script=$BASH_SOURCE
                elif test -n "$TMOUT"; then script=${.sh.file}
                elif test -n "$ZSH_NAME" ; then script=${(%):-%x}
                elif test ${0##*/} = dash; then x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}
                else script=$0
                fi

                echo $script


                Output for these shells:



                BASH source: ./myscript
                ZSH source: ./myscript
                KSH source: /home/pbrannan/git/theme/src/theme/web/myscript
                DASH source: /home/pbrannan/git/theme/src/theme/web/myscript
                BASH: ./myscript
                ZSH: ./myscript
                KSH: /home/pbrannan/git/theme/src/theme/web/myscript
                DASH: ./myscript


                I tried to make it work for csh/tcsh, but it's too hard; I'm sticking to POSIX.






                share|improve this answer













                This worked for me in bash, dash, ksh, and zsh:



                if test -n "$BASH" ; then script=$BASH_SOURCE
                elif test -n "$TMOUT"; then script=${.sh.file}
                elif test -n "$ZSH_NAME" ; then script=${(%):-%x}
                elif test ${0##*/} = dash; then x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}
                else script=$0
                fi

                echo $script


                Output for these shells:



                BASH source: ./myscript
                ZSH source: ./myscript
                KSH source: /home/pbrannan/git/theme/src/theme/web/myscript
                DASH source: /home/pbrannan/git/theme/src/theme/web/myscript
                BASH: ./myscript
                ZSH: ./myscript
                KSH: /home/pbrannan/git/theme/src/theme/web/myscript
                DASH: ./myscript


                I tried to make it work for csh/tcsh, but it's too hard; I'm sticking to POSIX.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Mar 15 '17 at 15:49









                Paul BrannanPaul Brannan

                20017




                20017























                    1














                    I was a bit confused by the community wiki answer (from Shawn J. Goff), so I wrote a script to sort things out. About $_, I found this: Usage of _ as an environment variable passed to a command. It's an environment variable so it's easy to test its value incorrectly.



                    Below is the script, then it's output. They also are in this gist.



                    test-shell-default-variables.sh





                    #!/bin/bash

                    # test-shell-default-variables.sh

                    # Usage examples (you might want to `sudo apt install zsh ksh`):
                    #
                    # ./test-shell-default-variables.sh dash bash
                    # ./test-shell-default-variables.sh dash bash zsh ksh
                    # ./test-shell-default-variables.sh dash bash zsh ksh | less -R

                    # `-R` in `less -R` to have less pass escape sequences directly to the terminal
                    # so we have colors.


                    # The "invoking with name `sh`" tests are commented because for every shell I
                    # tested (dash, bash, zsh and ksh), the output was the same as that of dash.

                    # The `test_expression` function also work with expansion changes. You can try
                    # lines like `test_expression '{BASH_SOURCE:-$0}'`.

                    echolor() {
                    echo -e "e[1;36m$@e[0m"
                    }

                    tell_file() {
                    echo File `"$1"` is:
                    echo ```
                    cat "$1"
                    echo ```
                    echo
                    }

                    SHELL_ARRAY=("$@")

                    test_command() {
                    for shell in "${SHELL_ARRAY[@]}"
                    do
                    prepare "$shell"
                    cmd="$(eval echo $1)"
                    # echo "cmd: $cmd"
                    printf '%-4s: ' "$shell"
                    { env -i $cmd 2>&1 1>&3 | sed 's/^/[err]/'; } 3>&1
                    teardown
                    done
                    echo
                    }

                    prepare () {
                    shell="$1"
                    PATH="$PWD/$shell/sh:$PATH"
                    }

                    teardown() {
                    PATH="${PATH#*:}"
                    }


                    ###
                    ### prepare
                    ###
                    for shell in "${SHELL_ARRAY[@]}"
                    do
                    mkdir "$shell"
                    ln -sT "/bin/$shell" "$shell/sh"
                    done

                    echo > printer.sh
                    echo '. ./printer.sh' > sourcer.sh
                    rm linked.sh &>/dev/null; ln -sT "printer.sh" "linked.sh"

                    tell_file sourcer.sh

                    ###
                    ### run
                    ###
                    test_expression() {
                    local expr="$1"

                    # prepare
                    echo "echo $expr" > printer.sh
                    tell_file printer.sh

                    # run
                    cmd='$shell ./printer.sh'
                    echolor "`$cmd` (simple invocation) ($expr):"
                    test_command "$cmd"

                    # cmd='sh ./printer.sh'
                    # echolor "`$cmd` (when executable name is `sh`) ($expr):"
                    # test_command "$cmd"

                    cmd='$shell ./sourcer.sh'
                    echolor "`$cmd` (via sourcing) ($expr):"
                    test_command "$cmd"

                    # cmd='sh ./sourcer.sh'
                    # echolor "`$cmd` (via sourcing, when name is `sh`) ($expr):"
                    # test_command "$cmd"

                    cmd='$shell ./linked.sh'
                    echolor "`$cmd` (via symlink) ($expr):"
                    test_command "$cmd"

                    # cmd='sh ./linked.sh'
                    # echolor "`$cmd` (via symlink, when name is `sh`) ($expr):"
                    # test_command "$cmd"

                    echolor "------------------------------------------"
                    echo
                    }

                    test_expression '$BASH_SOURCE'
                    test_expression '$0'
                    test_expression '$(/bin/true x y; true a b c; echo $_)' # Rq: true is a builtin
                    test_expression '$_'

                    ###
                    ### teardown
                    ###
                    for shell in "${SHELL_ARRAY[@]}"
                    do
                    rm "$shell/sh"
                    rm -d "$shell"
                    done

                    rm sourcer.sh
                    rm linked.sh
                    rm printer.sh


                    Output of ./test-shell-default-variables.sh {da,ba,z,k}sh





                    File `sourcer.sh` is:
                    ```
                    . ./printer.sh
                    ```

                    File `printer.sh` is:
                    ```
                    echo $BASH_SOURCE
                    ```

                    `$shell ./printer.sh` (simple invocation) ($BASH_SOURCE):
                    dash:
                    bash: ./printer.sh
                    zsh :
                    ksh :

                    `$shell ./sourcer.sh` (via sourcing) ($BASH_SOURCE):
                    dash:
                    bash: ./printer.sh
                    zsh :
                    ksh :

                    `$shell ./linked.sh` (via symlink) ($BASH_SOURCE):
                    dash:
                    bash: ./linked.sh
                    zsh :
                    ksh :

                    ------------------------------------------

                    File `printer.sh` is:
                    ```
                    echo $0
                    ```

                    `$shell ./printer.sh` (simple invocation) ($0):
                    dash: ./printer.sh
                    bash: ./printer.sh
                    zsh : ./printer.sh
                    ksh : ./printer.sh

                    `$shell ./sourcer.sh` (via sourcing) ($0):
                    dash: ./sourcer.sh
                    bash: ./sourcer.sh
                    zsh : ./printer.sh
                    ksh : ./sourcer.sh

                    `$shell ./linked.sh` (via symlink) ($0):
                    dash: ./linked.sh
                    bash: ./linked.sh
                    zsh : ./linked.sh
                    ksh : ./linked.sh

                    ------------------------------------------

                    File `printer.sh` is:
                    ```
                    echo $(/bin/true x y; true a b c; echo $_)
                    ```

                    `$shell ./printer.sh` (simple invocation) ($(/bin/true x y; true a b c; echo $_)):
                    dash:
                    bash: c
                    zsh : c
                    ksh :

                    `$shell ./sourcer.sh` (via sourcing) ($(/bin/true x y; true a b c; echo $_)):
                    dash:
                    bash: c
                    zsh : c
                    ksh :

                    `$shell ./linked.sh` (via symlink) ($(/bin/true x y; true a b c; echo $_)):
                    dash:
                    bash: c
                    zsh : c
                    ksh :

                    ------------------------------------------

                    File `printer.sh` is:
                    ```
                    echo $_
                    ```

                    `$shell ./printer.sh` (simple invocation) ($_):
                    dash:
                    bash: bash
                    zsh :
                    ksh :

                    `$shell ./sourcer.sh` (via sourcing) ($_):
                    dash:
                    bash: bash
                    zsh : ./printer.sh
                    ksh :

                    `$shell ./linked.sh` (via symlink) ($_):
                    dash:
                    bash: bash
                    zsh :
                    ksh :

                    ------------------------------------------


                    What did we learn ?



                    $BASH_SOURCE





                    • $BASH_SOURCE works in bash and only in bash.

                    • The only difference with $0 is when the current file was sourced by
                      another file. In that case, $BASH_PROFILE contains the name of the
                      sourced file, rather than that of the souring file.


                    $0




                    • In zsh, $0 has the same value as $BASH_SOURCE in bash.


                    $_





                    • $_ is left untouched by dash and ksh.

                    • In bash and zsh, $_ decays to the last argument of the last call.

                    • bash initializes $_ to "bash".

                    • zsh leaves $_ untouched.
                      (when sourcing, it`s just the result of the "last argument" rule).


                    Symlinks




                    • When a script is called through a symlink, no variable contains any
                      reference to the destination of the link, only its name.


                    ksh




                    • Regarding those tests, ksh behaves like dash.


                    sh




                    • When bash or zsh is called through a symlink named sh, regarding those tests, it behave like dash.






                    share|improve this answer




























                      1














                      I was a bit confused by the community wiki answer (from Shawn J. Goff), so I wrote a script to sort things out. About $_, I found this: Usage of _ as an environment variable passed to a command. It's an environment variable so it's easy to test its value incorrectly.



                      Below is the script, then it's output. They also are in this gist.



                      test-shell-default-variables.sh





                      #!/bin/bash

                      # test-shell-default-variables.sh

                      # Usage examples (you might want to `sudo apt install zsh ksh`):
                      #
                      # ./test-shell-default-variables.sh dash bash
                      # ./test-shell-default-variables.sh dash bash zsh ksh
                      # ./test-shell-default-variables.sh dash bash zsh ksh | less -R

                      # `-R` in `less -R` to have less pass escape sequences directly to the terminal
                      # so we have colors.


                      # The "invoking with name `sh`" tests are commented because for every shell I
                      # tested (dash, bash, zsh and ksh), the output was the same as that of dash.

                      # The `test_expression` function also work with expansion changes. You can try
                      # lines like `test_expression '{BASH_SOURCE:-$0}'`.

                      echolor() {
                      echo -e "e[1;36m$@e[0m"
                      }

                      tell_file() {
                      echo File `"$1"` is:
                      echo ```
                      cat "$1"
                      echo ```
                      echo
                      }

                      SHELL_ARRAY=("$@")

                      test_command() {
                      for shell in "${SHELL_ARRAY[@]}"
                      do
                      prepare "$shell"
                      cmd="$(eval echo $1)"
                      # echo "cmd: $cmd"
                      printf '%-4s: ' "$shell"
                      { env -i $cmd 2>&1 1>&3 | sed 's/^/[err]/'; } 3>&1
                      teardown
                      done
                      echo
                      }

                      prepare () {
                      shell="$1"
                      PATH="$PWD/$shell/sh:$PATH"
                      }

                      teardown() {
                      PATH="${PATH#*:}"
                      }


                      ###
                      ### prepare
                      ###
                      for shell in "${SHELL_ARRAY[@]}"
                      do
                      mkdir "$shell"
                      ln -sT "/bin/$shell" "$shell/sh"
                      done

                      echo > printer.sh
                      echo '. ./printer.sh' > sourcer.sh
                      rm linked.sh &>/dev/null; ln -sT "printer.sh" "linked.sh"

                      tell_file sourcer.sh

                      ###
                      ### run
                      ###
                      test_expression() {
                      local expr="$1"

                      # prepare
                      echo "echo $expr" > printer.sh
                      tell_file printer.sh

                      # run
                      cmd='$shell ./printer.sh'
                      echolor "`$cmd` (simple invocation) ($expr):"
                      test_command "$cmd"

                      # cmd='sh ./printer.sh'
                      # echolor "`$cmd` (when executable name is `sh`) ($expr):"
                      # test_command "$cmd"

                      cmd='$shell ./sourcer.sh'
                      echolor "`$cmd` (via sourcing) ($expr):"
                      test_command "$cmd"

                      # cmd='sh ./sourcer.sh'
                      # echolor "`$cmd` (via sourcing, when name is `sh`) ($expr):"
                      # test_command "$cmd"

                      cmd='$shell ./linked.sh'
                      echolor "`$cmd` (via symlink) ($expr):"
                      test_command "$cmd"

                      # cmd='sh ./linked.sh'
                      # echolor "`$cmd` (via symlink, when name is `sh`) ($expr):"
                      # test_command "$cmd"

                      echolor "------------------------------------------"
                      echo
                      }

                      test_expression '$BASH_SOURCE'
                      test_expression '$0'
                      test_expression '$(/bin/true x y; true a b c; echo $_)' # Rq: true is a builtin
                      test_expression '$_'

                      ###
                      ### teardown
                      ###
                      for shell in "${SHELL_ARRAY[@]}"
                      do
                      rm "$shell/sh"
                      rm -d "$shell"
                      done

                      rm sourcer.sh
                      rm linked.sh
                      rm printer.sh


                      Output of ./test-shell-default-variables.sh {da,ba,z,k}sh





                      File `sourcer.sh` is:
                      ```
                      . ./printer.sh
                      ```

                      File `printer.sh` is:
                      ```
                      echo $BASH_SOURCE
                      ```

                      `$shell ./printer.sh` (simple invocation) ($BASH_SOURCE):
                      dash:
                      bash: ./printer.sh
                      zsh :
                      ksh :

                      `$shell ./sourcer.sh` (via sourcing) ($BASH_SOURCE):
                      dash:
                      bash: ./printer.sh
                      zsh :
                      ksh :

                      `$shell ./linked.sh` (via symlink) ($BASH_SOURCE):
                      dash:
                      bash: ./linked.sh
                      zsh :
                      ksh :

                      ------------------------------------------

                      File `printer.sh` is:
                      ```
                      echo $0
                      ```

                      `$shell ./printer.sh` (simple invocation) ($0):
                      dash: ./printer.sh
                      bash: ./printer.sh
                      zsh : ./printer.sh
                      ksh : ./printer.sh

                      `$shell ./sourcer.sh` (via sourcing) ($0):
                      dash: ./sourcer.sh
                      bash: ./sourcer.sh
                      zsh : ./printer.sh
                      ksh : ./sourcer.sh

                      `$shell ./linked.sh` (via symlink) ($0):
                      dash: ./linked.sh
                      bash: ./linked.sh
                      zsh : ./linked.sh
                      ksh : ./linked.sh

                      ------------------------------------------

                      File `printer.sh` is:
                      ```
                      echo $(/bin/true x y; true a b c; echo $_)
                      ```

                      `$shell ./printer.sh` (simple invocation) ($(/bin/true x y; true a b c; echo $_)):
                      dash:
                      bash: c
                      zsh : c
                      ksh :

                      `$shell ./sourcer.sh` (via sourcing) ($(/bin/true x y; true a b c; echo $_)):
                      dash:
                      bash: c
                      zsh : c
                      ksh :

                      `$shell ./linked.sh` (via symlink) ($(/bin/true x y; true a b c; echo $_)):
                      dash:
                      bash: c
                      zsh : c
                      ksh :

                      ------------------------------------------

                      File `printer.sh` is:
                      ```
                      echo $_
                      ```

                      `$shell ./printer.sh` (simple invocation) ($_):
                      dash:
                      bash: bash
                      zsh :
                      ksh :

                      `$shell ./sourcer.sh` (via sourcing) ($_):
                      dash:
                      bash: bash
                      zsh : ./printer.sh
                      ksh :

                      `$shell ./linked.sh` (via symlink) ($_):
                      dash:
                      bash: bash
                      zsh :
                      ksh :

                      ------------------------------------------


                      What did we learn ?



                      $BASH_SOURCE





                      • $BASH_SOURCE works in bash and only in bash.

                      • The only difference with $0 is when the current file was sourced by
                        another file. In that case, $BASH_PROFILE contains the name of the
                        sourced file, rather than that of the souring file.


                      $0




                      • In zsh, $0 has the same value as $BASH_SOURCE in bash.


                      $_





                      • $_ is left untouched by dash and ksh.

                      • In bash and zsh, $_ decays to the last argument of the last call.

                      • bash initializes $_ to "bash".

                      • zsh leaves $_ untouched.
                        (when sourcing, it`s just the result of the "last argument" rule).


                      Symlinks




                      • When a script is called through a symlink, no variable contains any
                        reference to the destination of the link, only its name.


                      ksh




                      • Regarding those tests, ksh behaves like dash.


                      sh




                      • When bash or zsh is called through a symlink named sh, regarding those tests, it behave like dash.






                      share|improve this answer


























                        1












                        1








                        1







                        I was a bit confused by the community wiki answer (from Shawn J. Goff), so I wrote a script to sort things out. About $_, I found this: Usage of _ as an environment variable passed to a command. It's an environment variable so it's easy to test its value incorrectly.



                        Below is the script, then it's output. They also are in this gist.



                        test-shell-default-variables.sh





                        #!/bin/bash

                        # test-shell-default-variables.sh

                        # Usage examples (you might want to `sudo apt install zsh ksh`):
                        #
                        # ./test-shell-default-variables.sh dash bash
                        # ./test-shell-default-variables.sh dash bash zsh ksh
                        # ./test-shell-default-variables.sh dash bash zsh ksh | less -R

                        # `-R` in `less -R` to have less pass escape sequences directly to the terminal
                        # so we have colors.


                        # The "invoking with name `sh`" tests are commented because for every shell I
                        # tested (dash, bash, zsh and ksh), the output was the same as that of dash.

                        # The `test_expression` function also work with expansion changes. You can try
                        # lines like `test_expression '{BASH_SOURCE:-$0}'`.

                        echolor() {
                        echo -e "e[1;36m$@e[0m"
                        }

                        tell_file() {
                        echo File `"$1"` is:
                        echo ```
                        cat "$1"
                        echo ```
                        echo
                        }

                        SHELL_ARRAY=("$@")

                        test_command() {
                        for shell in "${SHELL_ARRAY[@]}"
                        do
                        prepare "$shell"
                        cmd="$(eval echo $1)"
                        # echo "cmd: $cmd"
                        printf '%-4s: ' "$shell"
                        { env -i $cmd 2>&1 1>&3 | sed 's/^/[err]/'; } 3>&1
                        teardown
                        done
                        echo
                        }

                        prepare () {
                        shell="$1"
                        PATH="$PWD/$shell/sh:$PATH"
                        }

                        teardown() {
                        PATH="${PATH#*:}"
                        }


                        ###
                        ### prepare
                        ###
                        for shell in "${SHELL_ARRAY[@]}"
                        do
                        mkdir "$shell"
                        ln -sT "/bin/$shell" "$shell/sh"
                        done

                        echo > printer.sh
                        echo '. ./printer.sh' > sourcer.sh
                        rm linked.sh &>/dev/null; ln -sT "printer.sh" "linked.sh"

                        tell_file sourcer.sh

                        ###
                        ### run
                        ###
                        test_expression() {
                        local expr="$1"

                        # prepare
                        echo "echo $expr" > printer.sh
                        tell_file printer.sh

                        # run
                        cmd='$shell ./printer.sh'
                        echolor "`$cmd` (simple invocation) ($expr):"
                        test_command "$cmd"

                        # cmd='sh ./printer.sh'
                        # echolor "`$cmd` (when executable name is `sh`) ($expr):"
                        # test_command "$cmd"

                        cmd='$shell ./sourcer.sh'
                        echolor "`$cmd` (via sourcing) ($expr):"
                        test_command "$cmd"

                        # cmd='sh ./sourcer.sh'
                        # echolor "`$cmd` (via sourcing, when name is `sh`) ($expr):"
                        # test_command "$cmd"

                        cmd='$shell ./linked.sh'
                        echolor "`$cmd` (via symlink) ($expr):"
                        test_command "$cmd"

                        # cmd='sh ./linked.sh'
                        # echolor "`$cmd` (via symlink, when name is `sh`) ($expr):"
                        # test_command "$cmd"

                        echolor "------------------------------------------"
                        echo
                        }

                        test_expression '$BASH_SOURCE'
                        test_expression '$0'
                        test_expression '$(/bin/true x y; true a b c; echo $_)' # Rq: true is a builtin
                        test_expression '$_'

                        ###
                        ### teardown
                        ###
                        for shell in "${SHELL_ARRAY[@]}"
                        do
                        rm "$shell/sh"
                        rm -d "$shell"
                        done

                        rm sourcer.sh
                        rm linked.sh
                        rm printer.sh


                        Output of ./test-shell-default-variables.sh {da,ba,z,k}sh





                        File `sourcer.sh` is:
                        ```
                        . ./printer.sh
                        ```

                        File `printer.sh` is:
                        ```
                        echo $BASH_SOURCE
                        ```

                        `$shell ./printer.sh` (simple invocation) ($BASH_SOURCE):
                        dash:
                        bash: ./printer.sh
                        zsh :
                        ksh :

                        `$shell ./sourcer.sh` (via sourcing) ($BASH_SOURCE):
                        dash:
                        bash: ./printer.sh
                        zsh :
                        ksh :

                        `$shell ./linked.sh` (via symlink) ($BASH_SOURCE):
                        dash:
                        bash: ./linked.sh
                        zsh :
                        ksh :

                        ------------------------------------------

                        File `printer.sh` is:
                        ```
                        echo $0
                        ```

                        `$shell ./printer.sh` (simple invocation) ($0):
                        dash: ./printer.sh
                        bash: ./printer.sh
                        zsh : ./printer.sh
                        ksh : ./printer.sh

                        `$shell ./sourcer.sh` (via sourcing) ($0):
                        dash: ./sourcer.sh
                        bash: ./sourcer.sh
                        zsh : ./printer.sh
                        ksh : ./sourcer.sh

                        `$shell ./linked.sh` (via symlink) ($0):
                        dash: ./linked.sh
                        bash: ./linked.sh
                        zsh : ./linked.sh
                        ksh : ./linked.sh

                        ------------------------------------------

                        File `printer.sh` is:
                        ```
                        echo $(/bin/true x y; true a b c; echo $_)
                        ```

                        `$shell ./printer.sh` (simple invocation) ($(/bin/true x y; true a b c; echo $_)):
                        dash:
                        bash: c
                        zsh : c
                        ksh :

                        `$shell ./sourcer.sh` (via sourcing) ($(/bin/true x y; true a b c; echo $_)):
                        dash:
                        bash: c
                        zsh : c
                        ksh :

                        `$shell ./linked.sh` (via symlink) ($(/bin/true x y; true a b c; echo $_)):
                        dash:
                        bash: c
                        zsh : c
                        ksh :

                        ------------------------------------------

                        File `printer.sh` is:
                        ```
                        echo $_
                        ```

                        `$shell ./printer.sh` (simple invocation) ($_):
                        dash:
                        bash: bash
                        zsh :
                        ksh :

                        `$shell ./sourcer.sh` (via sourcing) ($_):
                        dash:
                        bash: bash
                        zsh : ./printer.sh
                        ksh :

                        `$shell ./linked.sh` (via symlink) ($_):
                        dash:
                        bash: bash
                        zsh :
                        ksh :

                        ------------------------------------------


                        What did we learn ?



                        $BASH_SOURCE





                        • $BASH_SOURCE works in bash and only in bash.

                        • The only difference with $0 is when the current file was sourced by
                          another file. In that case, $BASH_PROFILE contains the name of the
                          sourced file, rather than that of the souring file.


                        $0




                        • In zsh, $0 has the same value as $BASH_SOURCE in bash.


                        $_





                        • $_ is left untouched by dash and ksh.

                        • In bash and zsh, $_ decays to the last argument of the last call.

                        • bash initializes $_ to "bash".

                        • zsh leaves $_ untouched.
                          (when sourcing, it`s just the result of the "last argument" rule).


                        Symlinks




                        • When a script is called through a symlink, no variable contains any
                          reference to the destination of the link, only its name.


                        ksh




                        • Regarding those tests, ksh behaves like dash.


                        sh




                        • When bash or zsh is called through a symlink named sh, regarding those tests, it behave like dash.






                        share|improve this answer













                        I was a bit confused by the community wiki answer (from Shawn J. Goff), so I wrote a script to sort things out. About $_, I found this: Usage of _ as an environment variable passed to a command. It's an environment variable so it's easy to test its value incorrectly.



                        Below is the script, then it's output. They also are in this gist.



                        test-shell-default-variables.sh





                        #!/bin/bash

                        # test-shell-default-variables.sh

                        # Usage examples (you might want to `sudo apt install zsh ksh`):
                        #
                        # ./test-shell-default-variables.sh dash bash
                        # ./test-shell-default-variables.sh dash bash zsh ksh
                        # ./test-shell-default-variables.sh dash bash zsh ksh | less -R

                        # `-R` in `less -R` to have less pass escape sequences directly to the terminal
                        # so we have colors.


                        # The "invoking with name `sh`" tests are commented because for every shell I
                        # tested (dash, bash, zsh and ksh), the output was the same as that of dash.

                        # The `test_expression` function also work with expansion changes. You can try
                        # lines like `test_expression '{BASH_SOURCE:-$0}'`.

                        echolor() {
                        echo -e "e[1;36m$@e[0m"
                        }

                        tell_file() {
                        echo File `"$1"` is:
                        echo ```
                        cat "$1"
                        echo ```
                        echo
                        }

                        SHELL_ARRAY=("$@")

                        test_command() {
                        for shell in "${SHELL_ARRAY[@]}"
                        do
                        prepare "$shell"
                        cmd="$(eval echo $1)"
                        # echo "cmd: $cmd"
                        printf '%-4s: ' "$shell"
                        { env -i $cmd 2>&1 1>&3 | sed 's/^/[err]/'; } 3>&1
                        teardown
                        done
                        echo
                        }

                        prepare () {
                        shell="$1"
                        PATH="$PWD/$shell/sh:$PATH"
                        }

                        teardown() {
                        PATH="${PATH#*:}"
                        }


                        ###
                        ### prepare
                        ###
                        for shell in "${SHELL_ARRAY[@]}"
                        do
                        mkdir "$shell"
                        ln -sT "/bin/$shell" "$shell/sh"
                        done

                        echo > printer.sh
                        echo '. ./printer.sh' > sourcer.sh
                        rm linked.sh &>/dev/null; ln -sT "printer.sh" "linked.sh"

                        tell_file sourcer.sh

                        ###
                        ### run
                        ###
                        test_expression() {
                        local expr="$1"

                        # prepare
                        echo "echo $expr" > printer.sh
                        tell_file printer.sh

                        # run
                        cmd='$shell ./printer.sh'
                        echolor "`$cmd` (simple invocation) ($expr):"
                        test_command "$cmd"

                        # cmd='sh ./printer.sh'
                        # echolor "`$cmd` (when executable name is `sh`) ($expr):"
                        # test_command "$cmd"

                        cmd='$shell ./sourcer.sh'
                        echolor "`$cmd` (via sourcing) ($expr):"
                        test_command "$cmd"

                        # cmd='sh ./sourcer.sh'
                        # echolor "`$cmd` (via sourcing, when name is `sh`) ($expr):"
                        # test_command "$cmd"

                        cmd='$shell ./linked.sh'
                        echolor "`$cmd` (via symlink) ($expr):"
                        test_command "$cmd"

                        # cmd='sh ./linked.sh'
                        # echolor "`$cmd` (via symlink, when name is `sh`) ($expr):"
                        # test_command "$cmd"

                        echolor "------------------------------------------"
                        echo
                        }

                        test_expression '$BASH_SOURCE'
                        test_expression '$0'
                        test_expression '$(/bin/true x y; true a b c; echo $_)' # Rq: true is a builtin
                        test_expression '$_'

                        ###
                        ### teardown
                        ###
                        for shell in "${SHELL_ARRAY[@]}"
                        do
                        rm "$shell/sh"
                        rm -d "$shell"
                        done

                        rm sourcer.sh
                        rm linked.sh
                        rm printer.sh


                        Output of ./test-shell-default-variables.sh {da,ba,z,k}sh





                        File `sourcer.sh` is:
                        ```
                        . ./printer.sh
                        ```

                        File `printer.sh` is:
                        ```
                        echo $BASH_SOURCE
                        ```

                        `$shell ./printer.sh` (simple invocation) ($BASH_SOURCE):
                        dash:
                        bash: ./printer.sh
                        zsh :
                        ksh :

                        `$shell ./sourcer.sh` (via sourcing) ($BASH_SOURCE):
                        dash:
                        bash: ./printer.sh
                        zsh :
                        ksh :

                        `$shell ./linked.sh` (via symlink) ($BASH_SOURCE):
                        dash:
                        bash: ./linked.sh
                        zsh :
                        ksh :

                        ------------------------------------------

                        File `printer.sh` is:
                        ```
                        echo $0
                        ```

                        `$shell ./printer.sh` (simple invocation) ($0):
                        dash: ./printer.sh
                        bash: ./printer.sh
                        zsh : ./printer.sh
                        ksh : ./printer.sh

                        `$shell ./sourcer.sh` (via sourcing) ($0):
                        dash: ./sourcer.sh
                        bash: ./sourcer.sh
                        zsh : ./printer.sh
                        ksh : ./sourcer.sh

                        `$shell ./linked.sh` (via symlink) ($0):
                        dash: ./linked.sh
                        bash: ./linked.sh
                        zsh : ./linked.sh
                        ksh : ./linked.sh

                        ------------------------------------------

                        File `printer.sh` is:
                        ```
                        echo $(/bin/true x y; true a b c; echo $_)
                        ```

                        `$shell ./printer.sh` (simple invocation) ($(/bin/true x y; true a b c; echo $_)):
                        dash:
                        bash: c
                        zsh : c
                        ksh :

                        `$shell ./sourcer.sh` (via sourcing) ($(/bin/true x y; true a b c; echo $_)):
                        dash:
                        bash: c
                        zsh : c
                        ksh :

                        `$shell ./linked.sh` (via symlink) ($(/bin/true x y; true a b c; echo $_)):
                        dash:
                        bash: c
                        zsh : c
                        ksh :

                        ------------------------------------------

                        File `printer.sh` is:
                        ```
                        echo $_
                        ```

                        `$shell ./printer.sh` (simple invocation) ($_):
                        dash:
                        bash: bash
                        zsh :
                        ksh :

                        `$shell ./sourcer.sh` (via sourcing) ($_):
                        dash:
                        bash: bash
                        zsh : ./printer.sh
                        ksh :

                        `$shell ./linked.sh` (via symlink) ($_):
                        dash:
                        bash: bash
                        zsh :
                        ksh :

                        ------------------------------------------


                        What did we learn ?



                        $BASH_SOURCE





                        • $BASH_SOURCE works in bash and only in bash.

                        • The only difference with $0 is when the current file was sourced by
                          another file. In that case, $BASH_PROFILE contains the name of the
                          sourced file, rather than that of the souring file.


                        $0




                        • In zsh, $0 has the same value as $BASH_SOURCE in bash.


                        $_





                        • $_ is left untouched by dash and ksh.

                        • In bash and zsh, $_ decays to the last argument of the last call.

                        • bash initializes $_ to "bash".

                        • zsh leaves $_ untouched.
                          (when sourcing, it`s just the result of the "last argument" rule).


                        Symlinks




                        • When a script is called through a symlink, no variable contains any
                          reference to the destination of the link, only its name.


                        ksh




                        • Regarding those tests, ksh behaves like dash.


                        sh




                        • When bash or zsh is called through a symlink named sh, regarding those tests, it behave like dash.







                        share|improve this answer












                        share|improve this answer



                        share|improve this answer










                        answered Nov 6 '18 at 22:45









                        Mathieu CAROFFMathieu CAROFF

                        2084




                        2084























                            0














                            For the bash shell, I found @Dennis Williamson's answer most helpful, but it didn't work in the case of sudo. This does:



                            if ( [[ $_ != $0 ]] && [[ $_ != $SHELL ]] ); then
                            echo "I'm being sourced!"
                            exit 1
                            fi





                            share|improve this answer






























                              0














                              For the bash shell, I found @Dennis Williamson's answer most helpful, but it didn't work in the case of sudo. This does:



                              if ( [[ $_ != $0 ]] && [[ $_ != $SHELL ]] ); then
                              echo "I'm being sourced!"
                              exit 1
                              fi





                              share|improve this answer




























                                0












                                0








                                0







                                For the bash shell, I found @Dennis Williamson's answer most helpful, but it didn't work in the case of sudo. This does:



                                if ( [[ $_ != $0 ]] && [[ $_ != $SHELL ]] ); then
                                echo "I'm being sourced!"
                                exit 1
                                fi





                                share|improve this answer















                                For the bash shell, I found @Dennis Williamson's answer most helpful, but it didn't work in the case of sudo. This does:



                                if ( [[ $_ != $0 ]] && [[ $_ != $SHELL ]] ); then
                                echo "I'm being sourced!"
                                exit 1
                                fi






                                share|improve this answer














                                share|improve this answer



                                share|improve this answer








                                edited Apr 13 '17 at 12:36









                                Community

                                1




                                1










                                answered Mar 21 '13 at 20:59









                                MattMatt

                                433410




                                433410























                                    0














                                    To make your script both bash- and zsh-compatible instead of using if statements you can simply write ${BASH_SOURCE[0]:-${(%):-%x}}. The resulting value will be taken from BASH_SOURCE[0] when it's defined, and ${(%):-%x}} when BASH_SOURCE[0] is not defined.






                                    share|improve this answer




























                                      0














                                      To make your script both bash- and zsh-compatible instead of using if statements you can simply write ${BASH_SOURCE[0]:-${(%):-%x}}. The resulting value will be taken from BASH_SOURCE[0] when it's defined, and ${(%):-%x}} when BASH_SOURCE[0] is not defined.






                                      share|improve this answer


























                                        0












                                        0








                                        0







                                        To make your script both bash- and zsh-compatible instead of using if statements you can simply write ${BASH_SOURCE[0]:-${(%):-%x}}. The resulting value will be taken from BASH_SOURCE[0] when it's defined, and ${(%):-%x}} when BASH_SOURCE[0] is not defined.






                                        share|improve this answer













                                        To make your script both bash- and zsh-compatible instead of using if statements you can simply write ${BASH_SOURCE[0]:-${(%):-%x}}. The resulting value will be taken from BASH_SOURCE[0] when it's defined, and ${(%):-%x}} when BASH_SOURCE[0] is not defined.







                                        share|improve this answer












                                        share|improve this answer



                                        share|improve this answer










                                        answered Feb 19 at 1:30









                                        dols3mdols3m

                                        1




                                        1























                                            0














                                            tl;dr script=$(readlink -e -- "${BASH_SOURCE}") (for bash obviously)







                                            $BASH_SOURCE test cases



                                            given file /tmp/source1.sh



                                            echo '$BASH_SOURCE '"(${BASH_SOURCE})"
                                            echo 'readlink -e $BASH_SOURCE'
                                            "($(readlink -e -- "${BASH_SOURCE}"))"


                                            source the file in different manners




                                            source from /tmp



                                            $> cd /tmp

                                            $> source source1.sh
                                            $BASH_SOURCE (source1.sh)
                                            readlink -e $BASH_SOURCE (/tmp/source1.sh)

                                            $> source ./source1.sh
                                            $BASH_SOURCE (./source1.sh)
                                            readlink -e $BASH_SOURCE (/tmp/source1.sh)

                                            $> source /tmp/source1.sh
                                            $BASH_SOURCE (/tmp/source1.sh)
                                            readlink -e $BASH_SOURCE (/tmp/source1.sh)




                                            source from /



                                            cd /
                                            $> source /tmp/source1.sh
                                            $0 (bash)
                                            $BASH_SOURCE (/tmp/source1.sh)
                                            readlink -e $BASH_SOURCE (/tmp/source1.sh)



                                            source from different relative paths /tmp/a and /var



                                            $> cd /tmp/a

                                            $> source ../source1.sh
                                            $BASH_SOURCE (../source1.sh)
                                            readlink -e $BASH_SOURCE (/tmp/source1.sh)

                                            $> cd /var

                                            $> source ../tmp/source1.sh
                                            $BASH_SOURCE (../tmp/source1.sh)
                                            readlink -e $BASH_SOURCE (/tmp/source1.sh)


                                            regarding $0



                                            in all cases, if the script had the added command



                                            echo '$0 '"(${0})"


                                            then source the script always printed



                                            $0 (bash)


                                            however, if the script was run, e.g.



                                            $> bash /tmp/source1.sh


                                            then $0 would be string value /tmp/source1.sh.



                                            $0 (/tmp/source1.sh)
                                            $BASH_SOURCE (/tmp/source1.sh)
                                            readlink -e $BASH_SOURCE (/tmp/source1.sh)





                                            share|improve this answer






























                                              0














                                              tl;dr script=$(readlink -e -- "${BASH_SOURCE}") (for bash obviously)







                                              $BASH_SOURCE test cases



                                              given file /tmp/source1.sh



                                              echo '$BASH_SOURCE '"(${BASH_SOURCE})"
                                              echo 'readlink -e $BASH_SOURCE'
                                              "($(readlink -e -- "${BASH_SOURCE}"))"


                                              source the file in different manners




                                              source from /tmp



                                              $> cd /tmp

                                              $> source source1.sh
                                              $BASH_SOURCE (source1.sh)
                                              readlink -e $BASH_SOURCE (/tmp/source1.sh)

                                              $> source ./source1.sh
                                              $BASH_SOURCE (./source1.sh)
                                              readlink -e $BASH_SOURCE (/tmp/source1.sh)

                                              $> source /tmp/source1.sh
                                              $BASH_SOURCE (/tmp/source1.sh)
                                              readlink -e $BASH_SOURCE (/tmp/source1.sh)




                                              source from /



                                              cd /
                                              $> source /tmp/source1.sh
                                              $0 (bash)
                                              $BASH_SOURCE (/tmp/source1.sh)
                                              readlink -e $BASH_SOURCE (/tmp/source1.sh)



                                              source from different relative paths /tmp/a and /var



                                              $> cd /tmp/a

                                              $> source ../source1.sh
                                              $BASH_SOURCE (../source1.sh)
                                              readlink -e $BASH_SOURCE (/tmp/source1.sh)

                                              $> cd /var

                                              $> source ../tmp/source1.sh
                                              $BASH_SOURCE (../tmp/source1.sh)
                                              readlink -e $BASH_SOURCE (/tmp/source1.sh)


                                              regarding $0



                                              in all cases, if the script had the added command



                                              echo '$0 '"(${0})"


                                              then source the script always printed



                                              $0 (bash)


                                              however, if the script was run, e.g.



                                              $> bash /tmp/source1.sh


                                              then $0 would be string value /tmp/source1.sh.



                                              $0 (/tmp/source1.sh)
                                              $BASH_SOURCE (/tmp/source1.sh)
                                              readlink -e $BASH_SOURCE (/tmp/source1.sh)





                                              share|improve this answer




























                                                0












                                                0








                                                0







                                                tl;dr script=$(readlink -e -- "${BASH_SOURCE}") (for bash obviously)







                                                $BASH_SOURCE test cases



                                                given file /tmp/source1.sh



                                                echo '$BASH_SOURCE '"(${BASH_SOURCE})"
                                                echo 'readlink -e $BASH_SOURCE'
                                                "($(readlink -e -- "${BASH_SOURCE}"))"


                                                source the file in different manners




                                                source from /tmp



                                                $> cd /tmp

                                                $> source source1.sh
                                                $BASH_SOURCE (source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)

                                                $> source ./source1.sh
                                                $BASH_SOURCE (./source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)

                                                $> source /tmp/source1.sh
                                                $BASH_SOURCE (/tmp/source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)




                                                source from /



                                                cd /
                                                $> source /tmp/source1.sh
                                                $0 (bash)
                                                $BASH_SOURCE (/tmp/source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)



                                                source from different relative paths /tmp/a and /var



                                                $> cd /tmp/a

                                                $> source ../source1.sh
                                                $BASH_SOURCE (../source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)

                                                $> cd /var

                                                $> source ../tmp/source1.sh
                                                $BASH_SOURCE (../tmp/source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)


                                                regarding $0



                                                in all cases, if the script had the added command



                                                echo '$0 '"(${0})"


                                                then source the script always printed



                                                $0 (bash)


                                                however, if the script was run, e.g.



                                                $> bash /tmp/source1.sh


                                                then $0 would be string value /tmp/source1.sh.



                                                $0 (/tmp/source1.sh)
                                                $BASH_SOURCE (/tmp/source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)





                                                share|improve this answer















                                                tl;dr script=$(readlink -e -- "${BASH_SOURCE}") (for bash obviously)







                                                $BASH_SOURCE test cases



                                                given file /tmp/source1.sh



                                                echo '$BASH_SOURCE '"(${BASH_SOURCE})"
                                                echo 'readlink -e $BASH_SOURCE'
                                                "($(readlink -e -- "${BASH_SOURCE}"))"


                                                source the file in different manners




                                                source from /tmp



                                                $> cd /tmp

                                                $> source source1.sh
                                                $BASH_SOURCE (source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)

                                                $> source ./source1.sh
                                                $BASH_SOURCE (./source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)

                                                $> source /tmp/source1.sh
                                                $BASH_SOURCE (/tmp/source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)




                                                source from /



                                                cd /
                                                $> source /tmp/source1.sh
                                                $0 (bash)
                                                $BASH_SOURCE (/tmp/source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)



                                                source from different relative paths /tmp/a and /var



                                                $> cd /tmp/a

                                                $> source ../source1.sh
                                                $BASH_SOURCE (../source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)

                                                $> cd /var

                                                $> source ../tmp/source1.sh
                                                $BASH_SOURCE (../tmp/source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)


                                                regarding $0



                                                in all cases, if the script had the added command



                                                echo '$0 '"(${0})"


                                                then source the script always printed



                                                $0 (bash)


                                                however, if the script was run, e.g.



                                                $> bash /tmp/source1.sh


                                                then $0 would be string value /tmp/source1.sh.



                                                $0 (/tmp/source1.sh)
                                                $BASH_SOURCE (/tmp/source1.sh)
                                                readlink -e $BASH_SOURCE (/tmp/source1.sh)






                                                share|improve this answer














                                                share|improve this answer



                                                share|improve this answer








                                                edited 2 hours ago

























                                                answered yesterday









                                                JamesThomasMoon1979JamesThomasMoon1979

                                                277211




                                                277211























                                                    -2














                                                    Actually, "dirname $0" will get you the path to the script, but you have to interpret it a bit:



                                                    $ cat bash0
                                                    #!/bin/bash
                                                    echo $0=$0
                                                    dirname $0
                                                    $ bash0 # "." appears in PATH right now.
                                                    $0=./bash0
                                                    .
                                                    $ ./bash0
                                                    $0=./bash0
                                                    .
                                                    $ $PWD/bash0
                                                    $0=/home/00/bediger/src/ksh/bash0
                                                    /home/00/bediger/src/ksh
                                                    $ $PWD/../ksh/bash0
                                                    $0=/home/00/bediger/src/ksh/../ksh/bash0
                                                    /home/00/bediger/src/ksh/../ksh
                                                    $ ../ksh/bash0
                                                    $0=../ksh/bash0
                                                    ../ksh


                                                    You have to prepare to handle "." as the directory name under some common circumstances. I'd experiment a bit, as I remember the dirname built-in to ksh doing things a bit differently when "." appears in PATH.






                                                    share|improve this answer



















                                                    • 4





                                                      This is a sourced script, not an executed script. $0 simply contains "bash" for an interactive shell, and that's all the sourced script sees.

                                                      – Cascabel
                                                      Dec 8 '10 at 16:32


















                                                    -2














                                                    Actually, "dirname $0" will get you the path to the script, but you have to interpret it a bit:



                                                    $ cat bash0
                                                    #!/bin/bash
                                                    echo $0=$0
                                                    dirname $0
                                                    $ bash0 # "." appears in PATH right now.
                                                    $0=./bash0
                                                    .
                                                    $ ./bash0
                                                    $0=./bash0
                                                    .
                                                    $ $PWD/bash0
                                                    $0=/home/00/bediger/src/ksh/bash0
                                                    /home/00/bediger/src/ksh
                                                    $ $PWD/../ksh/bash0
                                                    $0=/home/00/bediger/src/ksh/../ksh/bash0
                                                    /home/00/bediger/src/ksh/../ksh
                                                    $ ../ksh/bash0
                                                    $0=../ksh/bash0
                                                    ../ksh


                                                    You have to prepare to handle "." as the directory name under some common circumstances. I'd experiment a bit, as I remember the dirname built-in to ksh doing things a bit differently when "." appears in PATH.






                                                    share|improve this answer



















                                                    • 4





                                                      This is a sourced script, not an executed script. $0 simply contains "bash" for an interactive shell, and that's all the sourced script sees.

                                                      – Cascabel
                                                      Dec 8 '10 at 16:32
















                                                    -2












                                                    -2








                                                    -2







                                                    Actually, "dirname $0" will get you the path to the script, but you have to interpret it a bit:



                                                    $ cat bash0
                                                    #!/bin/bash
                                                    echo $0=$0
                                                    dirname $0
                                                    $ bash0 # "." appears in PATH right now.
                                                    $0=./bash0
                                                    .
                                                    $ ./bash0
                                                    $0=./bash0
                                                    .
                                                    $ $PWD/bash0
                                                    $0=/home/00/bediger/src/ksh/bash0
                                                    /home/00/bediger/src/ksh
                                                    $ $PWD/../ksh/bash0
                                                    $0=/home/00/bediger/src/ksh/../ksh/bash0
                                                    /home/00/bediger/src/ksh/../ksh
                                                    $ ../ksh/bash0
                                                    $0=../ksh/bash0
                                                    ../ksh


                                                    You have to prepare to handle "." as the directory name under some common circumstances. I'd experiment a bit, as I remember the dirname built-in to ksh doing things a bit differently when "." appears in PATH.






                                                    share|improve this answer













                                                    Actually, "dirname $0" will get you the path to the script, but you have to interpret it a bit:



                                                    $ cat bash0
                                                    #!/bin/bash
                                                    echo $0=$0
                                                    dirname $0
                                                    $ bash0 # "." appears in PATH right now.
                                                    $0=./bash0
                                                    .
                                                    $ ./bash0
                                                    $0=./bash0
                                                    .
                                                    $ $PWD/bash0
                                                    $0=/home/00/bediger/src/ksh/bash0
                                                    /home/00/bediger/src/ksh
                                                    $ $PWD/../ksh/bash0
                                                    $0=/home/00/bediger/src/ksh/../ksh/bash0
                                                    /home/00/bediger/src/ksh/../ksh
                                                    $ ../ksh/bash0
                                                    $0=../ksh/bash0
                                                    ../ksh


                                                    You have to prepare to handle "." as the directory name under some common circumstances. I'd experiment a bit, as I remember the dirname built-in to ksh doing things a bit differently when "." appears in PATH.







                                                    share|improve this answer












                                                    share|improve this answer



                                                    share|improve this answer










                                                    answered Dec 8 '10 at 16:15









                                                    Bruce EdigerBruce Ediger

                                                    35.3k667120




                                                    35.3k667120








                                                    • 4





                                                      This is a sourced script, not an executed script. $0 simply contains "bash" for an interactive shell, and that's all the sourced script sees.

                                                      – Cascabel
                                                      Dec 8 '10 at 16:32
















                                                    • 4





                                                      This is a sourced script, not an executed script. $0 simply contains "bash" for an interactive shell, and that's all the sourced script sees.

                                                      – Cascabel
                                                      Dec 8 '10 at 16:32










                                                    4




                                                    4





                                                    This is a sourced script, not an executed script. $0 simply contains "bash" for an interactive shell, and that's all the sourced script sees.

                                                    – Cascabel
                                                    Dec 8 '10 at 16:32







                                                    This is a sourced script, not an executed script. $0 simply contains "bash" for an interactive shell, and that's all the sourced script sees.

                                                    – Cascabel
                                                    Dec 8 '10 at 16:32




















                                                    draft saved

                                                    draft discarded




















































                                                    Thanks for contributing an answer to Unix & Linux Stack Exchange!


                                                    • Please be sure to answer the question. Provide details and share your research!

                                                    But avoid



                                                    • Asking for help, clarification, or responding to other answers.

                                                    • Making statements based on opinion; back them up with references or personal experience.


                                                    To learn more, see our tips on writing great answers.




                                                    draft saved


                                                    draft discarded














                                                    StackExchange.ready(
                                                    function () {
                                                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f4650%2fdetermining-path-to-sourced-shell-script%23new-answer', 'question_page');
                                                    }
                                                    );

                                                    Post as a guest















                                                    Required, but never shown





















































                                                    Required, but never shown














                                                    Required, but never shown












                                                    Required, but never shown







                                                    Required, but never shown

































                                                    Required, but never shown














                                                    Required, but never shown












                                                    Required, but never shown







                                                    Required, but never shown







                                                    Popular posts from this blog

                                                    How to make a Squid Proxy server?

                                                    第一次世界大戦

                                                    Touch on Surface Book