Tutorial :How do I know the script file name in a Bash script?

Question:

How can I determine the name of the Bash script file inside the script itself?

Like if my script is in file runme.sh, then how would I make it to display "You are running runme.sh" message without hardcoding that?

Solution:1

me=`basename "$0"`

For reading through a symlink, which is usually not what you want (you usually don't want to confuse the user this way), try:

me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")"

IMO, that'll produce confusing output. "I ran foo.sh, but it's saying I'm running bar.sh!? Must be a bug!" Besides, one of the purposes of having differently-named symlinks is to provide different functionality based on the name it's called as (think gzip and gunzip on some platforms).

Solution:3

Solution:4

If the script name has spaces in it, a more robust way is to use "$0" or "$(basename "$0")" - or on MacOS: "$(basename \"$0\")". This prevents the name from getting mangled or interpreted in any way. In general, it is good practice to always double-quote variable names in the shell.

Solution:5

$BASH_SOURCE gives the correct answer when sourcing the script.

This however includes the path so to get the scripts filename only, use:

$(basename $BASH_SOURCE)

Solution:6

If you want it without the path then you would use ${0##*/}

Solution:7

readlink prints out the value of a symbolic link. If it isn't a symbolic link, it prints the file name. -n tells it to not print a newline. -f tells it to follow the link completely (if a symbolic link was a link to another link, it would resolve that one as well).

Solution:8

These answers are correct for the cases they state but there is a still a problem if you run the script from another script using the 'source' keyword (so that it runs in the same shell). In this case, you get the $0 of the calling script. And in this case, I don't think it is possible to get the name of the script itself.

This is an edge case and should not be taken TOO seriously. If you run the script from another script directly (without 'source'), using $0 will work.

Solution:9

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.

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]

Solution:10

Re: Tanktalus's (accepted) answer above, a slightly cleaner way is to use:

me=$(readlink --canonicalize --no-newline $0)

If your script has been sourced from another bash script, you can use:

me=$(readlink --canonicalize --no-newline $BASH_SOURCE)

I agree that it would be confusing to dereference symlinks if your objective is to provide feedback to the user, but there are occasions when you do need to get the canonical name to a script or other file, and this is the best way, imo.

Solution:12

You can use $0 to determine your script name (with full path) - to get the script name only you can trim that variable with

basename $0

Solution:13

this="$(dirname "$(realpath "$BASH_SOURCE")")"

This resolves symbolic links (realpath does that), handles spaces (double quotes do this), and will find the current script name even when sourced (. ./myscript) or called by other scripts ($BASH_SOURCE handles that). After all that, it is good to save this in a environment variable for re-use or for easy copy elsewhere (this=)...

Solution:14

Since some comments asked about the filename without extension, here's an example how to accomplish that:

[EDIT] Per @ephemient in comments above, though the symbolic link thing may seem contrived, it is possible to fiddle with $0 such that it does not represent a filesystem resource. The OP is a bit ambiguous about what he wanted.