1

The Linux Documentation Project says:

When a file is sourced (by typing either source filename or . filename at the command line), the lines of code in the file are executed as if they were printed at the command line. This is particularly useful with complex prompts, to allow them to be stored in files and called up by sourcing the file they are in.

I have three scripts: test.sh, test2.sh and common.sh. common.sh sets up the variable $me containing the base name of the current script (using ${BASH_SOURCE[0]}). test.sh calls source on common.sh and displays $me. test2.sh calls eval on the contents of common.sh and displays $me.

==> common.sh <==
#!/bin/bash

realpath=$(realpath "${BASH_SOURCE[0]}")
me=$(basename "${realpath}")

==> test.sh <==
#!/bin/bash

source common.sh

echo "me: $me"

==> test2.sh <==
#!/bin/bash

common=$(cat common.sh)
eval "$common"

echo "me: $me"

When running ./test2.sh, the output is me: test2.sh. This is correct.

When running ./test.sh, the output is me: common.sh. Why is this the case?

EDIT

Jeff Schaller's answer is correct, in that sourcing another script "unshifts" the name of that script to the $BASH_SOURCE array. I was able to achieve what I wanted by looking at the last value in $BASH_SOURCE. See below:

#!/bin/bash

declare -p BASH_SOURCE
bash_source_size="${#BASH_SOURCE[*]}"
realpath=$(realpath "${BASH_SOURCE[$bash_source_size-1]}")
me=$(basename "${realpath}")
  • Just realpath=$(realpath "${BASH_SOURCE[-1]}") should be enough if the name of the most external sourced file is what you want. – sorontar Mar 9 '17 at 1:27
2

When test.sh calls source, bash is explicitly sourcing common.sh into the current script, and so updates the BASH_SOURCE variable. When test2.sh executes the command substitution (which could be anything) and subsequent eval, there is no explicit sourcing going on, so BASH_SOURCE is not affected.

Instrument your shell scripts with

declare -p BASH_SOURCE lines to see the difference:

$ ./test.sh
declare -a BASH_SOURCE='([0]="common.sh" [1]="./test.sh")'
declare -a BASH_SOURCE='([0]="./test.sh")'
me: common.sh

vs:

$ ./test2.sh
declare -a BASH_SOURCE='([0]="./test2.sh")'
declare -a BASH_SOURCE='([0]="./test2.sh")'
me: test2.sh

In test2.sh, for all bash knows, you ran an arbitrary command, i.e. $(echo ls).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.