determining path to sourced shell script
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
add a comment |
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
related question that has 4200+ upvotes: stackoverflow.com/q/59895/52074
– Trevor Boyd Smith
Jan 29 at 14:21
add a comment |
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
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
shell source
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
add a comment |
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
add a comment |
10 Answers
10
active
oldest
votes
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"
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.andsourceworked 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 doingsource, then doing.. I apologize for being incompetent. They are indeed identical. Anyway,$BASH_SOURCEworks.
– clacke
Mar 5 '13 at 7:35
|
show 15 more comments
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.
2
And note thatsourcesearches in$PATHif 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 likemydir="$(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
add a comment |
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
$
1
I don't understand: whycalled=$_; 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/shwhich 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
add a comment |
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]
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 invocationsource 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
add a comment |
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.
add a comment |
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_SOURCEworks in bash and only in bash.- The only difference with
$0is when the current file was sourced by
another file. In that case,$BASH_PROFILEcontains the name of the
sourced file, rather than that of the souring file.
$0
- In zsh,
$0has the same value as$BASH_SOURCEin 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.
add a comment |
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
add a comment |
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.
add a comment |
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)
add a comment |
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.
4
This is a sourced script, not an executed script.$0simply contains "bash" for an interactive shell, and that's all the sourced script sees.
– Cascabel
Dec 8 '10 at 16:32
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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"
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.andsourceworked 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 doingsource, then doing.. I apologize for being incompetent. They are indeed identical. Anyway,$BASH_SOURCEworks.
– clacke
Mar 5 '13 at 7:35
|
show 15 more comments
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"
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.andsourceworked 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 doingsource, then doing.. I apologize for being incompetent. They are indeed identical. Anyway,$BASH_SOURCEworks.
– clacke
Mar 5 '13 at 7:35
|
show 15 more comments
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"
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"
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.andsourceworked 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 doingsource, then doing.. I apologize for being incompetent. They are indeed identical. Anyway,$BASH_SOURCEworks.
– clacke
Mar 5 '13 at 7:35
|
show 15 more comments
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.andsourceworked 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 doingsource, then doing.. I apologize for being incompetent. They are indeed identical. Anyway,$BASH_SOURCEworks.
– 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
|
show 15 more comments
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.
2
And note thatsourcesearches in$PATHif 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 likemydir="$(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
add a comment |
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.
2
And note thatsourcesearches in$PATHif 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 likemydir="$(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
add a comment |
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.
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.
answered Dec 8 '10 at 15:52
pbmpbm
17.2k52847
17.2k52847
2
And note thatsourcesearches in$PATHif 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 likemydir="$(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
add a comment |
2
And note thatsourcesearches in$PATHif 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 likemydir="$(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
add a comment |
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
$
1
I don't understand: whycalled=$_; 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/shwhich 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
add a comment |
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
$
1
I don't understand: whycalled=$_; 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/shwhich 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
add a comment |
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
$
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
$
edited Nov 6 '18 at 17:49
community wiki
5 revs, 3 users 81%
Shawn J. Goff
1
I don't understand: whycalled=$_; 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/shwhich 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
add a comment |
1
I don't understand: whycalled=$_; 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/shwhich 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
add a comment |
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]
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 invocationsource 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
add a comment |
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]
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 invocationsource 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
add a comment |
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]
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]
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 invocationsource 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
add a comment |
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 invocationsource 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
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
answered Mar 15 '17 at 15:49
Paul BrannanPaul Brannan
20017
20017
add a comment |
add a comment |
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_SOURCEworks in bash and only in bash.- The only difference with
$0is when the current file was sourced by
another file. In that case,$BASH_PROFILEcontains the name of the
sourced file, rather than that of the souring file.
$0
- In zsh,
$0has the same value as$BASH_SOURCEin 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.
add a comment |
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_SOURCEworks in bash and only in bash.- The only difference with
$0is when the current file was sourced by
another file. In that case,$BASH_PROFILEcontains the name of the
sourced file, rather than that of the souring file.
$0
- In zsh,
$0has the same value as$BASH_SOURCEin 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.
add a comment |
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_SOURCEworks in bash and only in bash.- The only difference with
$0is when the current file was sourced by
another file. In that case,$BASH_PROFILEcontains the name of the
sourced file, rather than that of the souring file.
$0
- In zsh,
$0has the same value as$BASH_SOURCEin 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.
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_SOURCEworks in bash and only in bash.- The only difference with
$0is when the current file was sourced by
another file. In that case,$BASH_PROFILEcontains the name of the
sourced file, rather than that of the souring file.
$0
- In zsh,
$0has the same value as$BASH_SOURCEin 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.
answered Nov 6 '18 at 22:45
Mathieu CAROFFMathieu CAROFF
2084
2084
add a comment |
add a comment |
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
add a comment |
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
add a comment |
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
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
edited Apr 13 '17 at 12:36
Community♦
1
1
answered Mar 21 '13 at 20:59
MattMatt
433410
433410
add a comment |
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
answered Feb 19 at 1:30
dols3mdols3m
1
1
add a comment |
add a comment |
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)
add a comment |
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)
add a comment |
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)
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)
edited 2 hours ago
answered yesterday
JamesThomasMoon1979JamesThomasMoon1979
277211
277211
add a comment |
add a comment |
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.
4
This is a sourced script, not an executed script.$0simply contains "bash" for an interactive shell, and that's all the sourced script sees.
– Cascabel
Dec 8 '10 at 16:32
add a comment |
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.
4
This is a sourced script, not an executed script.$0simply contains "bash" for an interactive shell, and that's all the sourced script sees.
– Cascabel
Dec 8 '10 at 16:32
add a comment |
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.
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.
answered Dec 8 '10 at 16:15
Bruce EdigerBruce Ediger
35.3k667120
35.3k667120
4
This is a sourced script, not an executed script.$0simply contains "bash" for an interactive shell, and that's all the sourced script sees.
– Cascabel
Dec 8 '10 at 16:32
add a comment |
4
This is a sourced script, not an executed script.$0simply 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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
related question that has 4200+ upvotes: stackoverflow.com/q/59895/52074
– Trevor Boyd Smith
Jan 29 at 14:21