${BASH_SOURCE[0]} equivalent in zsh?

ShellZsh

Shell Problem Overview


The title should say it all. I'm looking for an equivalent to ${BASH_SOURCE[0]} in zsh.

Note: I keep finding "$0 is equivalent to ${BASH_SOURCE[0]}" around the Internet, but this seems to be false: $0 seems to be the name of the executing command. (It's argv[0], which makes sense.) Echoing $0 in my script (.zshrc) gives zsh for $0, which isn't the same as what ${BASH_SOURCE[0]} is. In fact, ${BASH_SOURCE[0]} seems to work in zsh, except for inside .zshrc files.

What I'm really doing in my .zshrc (that isn't working):

echo ${BASH_SOURCE[0]}
source `dirname $0`/common-shell-rc.sh

The source fails ($0 is zsh) and the echo outputs a blank line.

Edit: apparently, for $0 to work, I need the option FUNCTION_ARGZERO option set. Any way to test if this is set in a script? (so that I can temporarily set it) It is apparently on unless you set nofunction_argzero, and it is on in my shell. Still get nothing for $0. (I think b/c I'm not in a function.)

Shell Solutions


Solution 1 - Shell

${BASH_SOURCE[0]} equivalent in zsh is ${(%):-%N}, NOT $0(as OP said, the latter failed in .zshrc)

Here % indicates prompt expansion on the value, %N indicates "The name of the script, sourced file, or shell function that zsh is currently executing,

whichever was started most recently. If there is none, this is equivalent to the parameter $0."(from man zshmisc)

Solution 2 - Shell

${(%):-%x} is the closest zsh equivalent to bash's $BASH_SOURCE (and ksh's ${.sh.file}) - not $0.

Tip of the hat to Hui Zheng for providing the crucial pointer and background information in his answer.

It returns the (potentially relative) path of the enclosing script,

  • regardless of whether the script is being sourced or not.
    • specifically, it also works inside initialization/profiles files such as ~/.zshrc (unlike $0, which inexplicably returns the shell's path there).
  • regardless of whether called from inside a function defined in the script or not (unlike $0, which returns the function name inside a function).

The only difference to $BASH_SOURCE I've found is in the following obscure scenario - which may even be a bug (observed in zsh 5.0.5): inside a function nested inside another function in a sourced script, ${(%):-%x} does not return the enclosing script path when that nested function is called (again) later, after having been sourced (returns either nothing or 'zsh').


Background information on ${(%):-%x}:

  • (%):- in lieu of a variable name in a parameter (variable) expansion (${...}) makes escape sequences available that are normally used to represent environmental information in prompt strings, such as used in the PS1 variable to determine the string displayed as the primary interactive prompt.

    • % is an instance of a parameter expansion flag, all of which are listed in man zshexpn under the heading Parameter Expansion Flags.
  • %x is one of the escape sequences that can be used in prompt strings, and it functions as described above; there are many more, such as %d to represent the current dir.

    • man zshmisc lists all available sequences under the heading SIMPLE PROMPT ESCAPES.

Solution 3 - Shell

If you want to make your script both bash and zsh-compatible you can use ${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.

Solution 4 - Shell

$0 is correct. In a sourced script, this is the name of a script, as it was passed to the . or source built-in (so if the path_dirs option is set, you may need to do a $path lookup to find the actual location of the script).

.zshrc is not sourced, which explains why $0 is not set to .zshrc. You know the file name and location anyway: it's ${ZDOTDIR-~}/.zshrc.

Solution 5 - Shell

If you are symlinking to .zshrc in a dotfiles directory and want to reference other files in the directory, then try this:

SOURCE=${(%):-%N}
while [ -h "$SOURCE" ]; do
  DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
done
DOTFILES_DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"

(I got the loop script from here.)

Solution 6 - Shell

Maybe you're looking for $_?

# foo.sh
source foo2.sh

and

# foo2.sh
echo $_

yields

# ./foo.sh
foo2.sh

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionThanatosView Question on Stackoverflow
Solution 1 - ShellHui ZhengView Answer on Stackoverflow
Solution 2 - Shellmklement0View Answer on Stackoverflow
Solution 3 - Shelldols3mView Answer on Stackoverflow
Solution 4 - ShellGilles 'SO- stop being evil'View Answer on Stackoverflow
Solution 5 - ShellFrancis PotterView Answer on Stackoverflow
Solution 6 - ShellCodererView Answer on Stackoverflow