801

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?

2

26 Answers 26

831
me=$(basename "$0") 

For reading through a symlink1, 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).


1 That is, to resolve symlinks such that when the user executes foo.sh which is actually a symlink to bar.sh, you wish to use the resolved name bar.sh rather than foo.sh.

Sign up to request clarification or add additional context in comments.

10 Comments

$0 gives you the name via which the script was invoked, not the real path of the actual script file.
It works unless you're being called via symlink. But, even then, it's usually what you want anyway, IME.
Doesn't work for scripts which are sourced as opposed to invoked.
@Charles Duffy: See Dimitre Radoulov's answer below.
-1, 1. readlink will only travel one symlink deep, 2. $0 in the first example is subject to word splitting, 3. $0 is passed to basename, readlink, and echo in a position which allows it to be treated as a command line switch. I suggest instead me=$(basename -- "$0") or much more efficiently at the expense of readability, me=${0##*/}. For symlinks, me=$(basename -- "$(readlink -f -- "$0")") assuming gnu utils, otherwise it will be a very long script which I will not write here.
|
328
 # ------------- SCRIPT ------------- #  #!/bin/bash echo echo "# arguments called with ----> ${@} " echo "# \$1 ----------------------> $1 " echo "# \$2 ----------------------> $2 " echo "# path to me ---------------> ${0} " echo "# parent path --------------> ${0%/*} " echo "# my name ------------------> ${0##*/} " echo exit  # ------------- CALLED ------------- # # Notice on the next line, the first argument is called within double, # and single quotes, since it contains two words $ /misc/shell_scripts/check_root/show_parms.sh "'hello there'" "'william'" # ------------- RESULTS ------------- # # arguments called with ---> 'hello there' 'william' # $1 ----------------------> 'hello there' # $2 ----------------------> 'william' # path to me --------------> /misc/shell_scripts/check_root/show_parms.sh # parent path -------------> /misc/shell_scripts/check_root # my name -----------------> show_parms.sh # ------------- END ------------- # 

10 Comments

How do I get just show_params, i.e. the name without any optional extension?
Not working in case the script is invoked from another folder. The path is included in the ${0##*/}. Tested using GitBash.
The above didn't work for me from a .bash_login script, but the Dimitre Radoulov solution of $BASH_SOURCE works great.
@AlikElzin-kilaka For me, and 5 years later, and with native bash 5.0-4, it works, even when the script is called from another folder.
|
236

With bash >= 3 the following works:

$ ./s 0 is: ./s BASH_SOURCE is: ./s $ . ./s 0 is: bash BASH_SOURCE is: ./s $ cat s #!/bin/bash printf '$0 is: %s\n$BASH_SOURCE is: %s\n' "$0" "$BASH_SOURCE" 

9 Comments

Great! That's the answer which works for both ./scrip.sh and source ./script.sh
This is what I want, and it is easily to use "dirname $BASE_SOURCE" to get directory that the scripts located.
I almost learnt that difference the hard way when writing a self-deleting script. Luckily 'rm' was aliased to 'rm -i' :)
anyway to the . ./s to get the name ./s instead of bash? I've found that $1 is not always set to ./s...
It's in BASH_SOURCE, isn't it?
|
142

$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) 

4 Comments

This answer is IMHO the best one because teh solution makes use of self-documenting code. $BASH_SOURCE is totally understandable without reading any documentation whereas e.g. ${0##*/} is not
This answer is of more value i belief because if we run like . <filename> [arguments], $0 would give name of caller shell. Well at least on OSX for sure.
@AndreasM.Oberheim the drawback of basename is that a separate process has to be forked, whereas ##*/ is just bash's own processing. You can still use $BASH_SOURCE with it, though: ${BASH_SOURCE[0]##*/}
Further drawbacks are script names or paths containing spaces or starting with dashes. To avoid trouble with those, it should be $( basename -- "$BASH_SOURCE" ).
74

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.

2 Comments

+1 for direct answer + concise. if needing symlink features I suggest seeing: Travis B. Hartwell's answer.
Even more robustly: $(basename -- "$0") in case the script name starts with dash. E.g.: echo 'echo $(basename -- "$0")' > --test.sh; bash -- --test.sh
37

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

2 Comments

And what if I want it without any optional file extension?
To remove an extension, you can try "${VARIABLE%.ext}" where VARIABLE is the value you got from ${0##*/} and ".ext" is the extension you want to remove.
24

To answer Chris Conway, on Linux (at least) you would do this:

echo $(basename $(readlink -nf $0)) 

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).

1 Comment

-n is harmless but not necessary because $(...) structure will trim it.
21

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]

2 Comments

It works if you source in file a (assume a's contents is echo "${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}") from an interactive session -- then it will give you a's path. But if you write a script b with source a in it and run ./b, it'll return b's path.
Doesn't your answer give the FIRST file rather than the last/most current file? I'm finding that ${BASH_SOURCE[0]}" or just $BASH_SOURCE tells me the current file - i.e. the answer @Zainka posted.
21

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

FileName=${0##*/} FileNameWithoutExtension=${FileName%.*} 

Enjoy!

3 Comments

this is black magic!
What the feature in your example is called i.e. ${more_stuff_than_just_a_variable} ?
@RobertLugg: it's called "bash parameter substitution", there should be a ton of material if you search for that term :)
13

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.

1 Comment

You have a very good point. Not an edge case IMO. There is a solution though: See Dimitre Radoulov's answer above
12

This works fine with ./self.sh, ~/self.sh, source self.sh, source ~/self.sh:

#!/usr/bin/env bash self=$(readlink -f "${BASH_SOURCE[0]}") basename=$(basename "$self") echo "$self" echo "$basename" 

Credits: I combined multiple answers to get this one.

1 Comment

$(readlink -f "${BASH_SOURCE[0]}") only works if you are using bash. $(readlink -f "${BASH_SOURCE:-$0}") works regardless.
11

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.

Comments

10
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=)...

2 Comments

FYI realpath is not a built-in BASH command. It is a standalone executable that is available only in certain distributions
Can confirm that, in my 14.04 box it's not available and I had to use readlink instead
9

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 

Comments

9

if your invoke shell script like

/home/mike/runme.sh 

$0 is full name

 /home/mike/runme.sh 

basename $0 will get the base file name

 runme.sh 

and you need to put this basic name into a variable like

filename=$(basename $0) 

and add your additional text

echo "You are running $filename" 

so your scripts like

/home/mike/runme.sh #!/bin/bash filename=$(basename $0) echo "You are running $filename" 

Comments

3

Short, clear and simple, in my_script.sh

#!/bin/bash running_file_name=$(basename "$0") echo "You are running '$running_file_name' file." 

Out put:

./my_script.sh You are running 'my_script.sh' file. 

Comments

2
echo "$(basename "`test -L ${BASH_SOURCE[0]} \ && readlink ${BASH_SOURCE[0]} \ || echo ${BASH_SOURCE[0]}`")" 

Comments

2

In bash you can get the script file name using $0. Generally $1, $2 etc are to access CLI arguments. Similarly $0 is to access the name which triggers the script(script file name).

#!/bin/bash echo "You are running $0" ... ... 

If you invoke the script with path like /path/to/script.sh then $0 also will give the filename with path. In that case need to use $(basename $0) to get only script file name.

Comments

1

Info thanks to Bill Hernandez. I added some preferences I'm adopting.

#!/bin/bash function Usage(){ echo " Usage: show_parameters [ arg1 ][ arg2 ]" } [[ ${#2} -eq 0 ]] && Usage || { echo echo "# arguments called with ----> ${@} " echo "# \$1 -----------------------> $1 " echo "# \$2 -----------------------> $2 " echo "# path to me ---------------> ${0} " | sed "s/$USER/\$USER/g" echo "# parent path --------------> ${0%/*} " | sed "s/$USER/\$USER/g" echo "# my name ------------------> ${0##*/} " echo } 

Cheers

Comments

1

To get the "realpath" of script or sourced scripts in all cases :

fullname=$(readlink $0) # Take care of symbolic links dirname=${fullname%/*} # Get (most of the time) the dirname realpath=$(dirname $BASH_SOURCE) # TO handle sourced scripts [ "$realpath" = '.' ] && realpath=${dirname:-.} 

Here is the bash script to generate (in a newly created "workdir" subdir and in "mytest" in current dir), a bash script which in turn will source another script, which in turm will call a bash defined function .... tested with many ways to launch them :

#!/bin/bash ############################################################## ret=0 fullname=$(readlink $0) # Take care of symbolic links dirname=${fullname%/*} # Get (most of the time) the dirname realpath=$(dirname $BASH_SOURCE) # TO handle sourced scripts [ "$realpath" = '.' ] && realpath=${dirname:-.} fullname_withoutextension=${fullname%.*} mkdir -p workdir cat <<'EOD' > workdir/_script_.sh #!/bin/bash ############################################################## ret=0 fullname=$(readlink $0) # Take care of symbolic links dirname=${fullname%/*} # Get (most of the time) the dirname realpath=$(dirname $BASH_SOURCE) # TO handle sourced scripts [ "$realpath" = '.' ] && realpath=${dirname:-.} fullname_withoutextension=${fullname%.*} echo echo "# ------------- RESULTS ------------- #" echo "# path to me (\$0)-----------> ${0} " echo "# arguments called with ----> ${@} " echo "# \$1 -----------------------> $1 " echo "# \$2 -----------------------> $2 " echo "# path to me (\$fullname)----> ${fullname} " echo "# parent path(\${0%/*})------> ${0%/*} " echo "# parent path(\$dirname)-----> ${dirname} " echo "# my name ----\${0##*/}------> ${0##*/} " echo "# my source -\${BASH_SOURCE}-> ${BASH_SOURCE} " echo "# parent path(from BASH_SOURCE) -> $(dirname $BASH_SOURCE)" echo "# my function name -\${FUNCNAME[0]}------> ${FUNCNAME[0]}" echo "# my source or script real path (realpath)------------------> $realpath" echo [ "$realpath" = "workdir" ] || ret=1 [ $ret = 0 ] || echo "*******************************************************" [ $ret = 0 ] || echo "*********** ERROR **********************************" [ $ret = 0 ] || echo "*******************************************************" show_params () { echo echo "# --- RESULTS FROM show_params() ---- #" echo "# path to me (\$0)-----------> ${0} " echo "# arguments called with ----> ${@} " echo "# \$1 -----------------------> $1 " echo "# \$2 -----------------------> $2 " echo "# path to me (\$fullname)----> ${fullname} " echo "# parent path(\${0%/*})------> ${0%/*} " echo "# parent path(\$dirname)-----> ${dirname} " echo "# my name ----\${0##*/}------> ${0##*/} " echo "# my source -\${BASH_SOURCE}-> ${BASH_SOURCE} " echo "# parent path(from BASH_SOURCE) -> $(dirname $BASH_SOURCE)" echo "# my function name -\${FUNCNAME[0]}------> ${FUNCNAME[0]}" echo "# my source or script real path (realpath)------------------> $realpath" echo [ "$realpath" = "workdir" ] || ret=1 [ $ret = 0 ] || echo "*******************************************************" [ $ret = 0 ] || echo "*********** ERROR **********************************" [ $ret = 0 ] || echo "*******************************************************" } show_params "$@" EOD cat workdir/_script_.sh > workdir/_side_by_side_script_sourced.inc cat <<'EOD' >> workdir/_script_.sh echo "# . $realpath/_side_by_side_script_sourced.inc 'hello there' 'william'" . $realpath/_side_by_side_script_sourced.inc 'hello there' 'william' [ $ret = 0 ] || echo "*******************************************************" [ $ret = 0 ] || echo "*********** ERROR **********************************" [ $ret = 0 ] || echo "*******************************************************" EOD chmod +x workdir/_script_.sh [ -L _mytest_ ] && rm _mytest_ ln -s workdir/_script_.sh _mytest_ # ------------- CALLED ------------- # called_by () { echo '==========================================================================' echo " Called by : " "$@" echo '==========================================================================' eval "$@" } called_by bash _mytest_ called_by ./_mytest_ called_by bash workdir/_script_.sh called_by workdir/_script_.sh called_by . workdir/_script_.sh # ------------- RESULTS ------------- # echo echo [ $ret = 0 ] || echo "*******************************************************" [ $ret = 0 ] || echo "*********** ERROR **********************************" [ $ret = 0 ] || echo "*******************************************************" echo [ $ret = 0 ] && echo ".... location of scripts (\$realpath) should always be equal to $realpath, for all test cases at date". echo # ------------- END ------------- # 

Comments

0
DIRECTORY=$(cd `dirname $0` && pwd) 

I got the above from another Stack Overflow question, Can a Bash script tell what directory it's stored in?, but I think it's useful for this topic as well.

Comments

0

Here is what I came up with, inspired by Dimitre Radoulov's answer (which I upvoted, by the way).

script="$BASH_SOURCE" [ -z "$BASH_SOURCE" ] && script="$0" echo "Called $script with $# argument(s)" 

regardless of the way you call your script

. path/to/script.sh 

or

./path/to/script.sh 

Comments

0

$0 will give the name of the script you are running. Create a script file and add following code

#!/bin/bash echo "Name of the file is $0" 

then run from terminal like this

./file_name.sh 

Comments

0

As an option not listed in above answers you can use script's pid to get a lot of info including name from /proc/$pid folder, like this:

#!/bin/bash pid=$$ # get script's pid to var $pid echo $pid # there is a file in /proc/$pid folder called cmdline # this file stores full command line used by script cat /proc/$pid/cmdline 

output:

$ ./test 1 '2 3' 4 12924 /bin/bash./test12 34 

the output looks a bit messy it's just one string... not exactly it actually has delimiters but to be able to see them we need to change our cat command like this:

#!/bin/bash pid=$$ # get script's pid to var $pid echo $pid # add -A - show all option for cat cat -A /proc/$pid/cmdline 

there ya go, now we can see delimeters^@:

$ ./test 1 '2 3' 4 13002 /bin/bash^@./test^@1^@2 3^@4^@ 

lets add some more hacking to get the name:

#!/bin/bash pid=$$ # get script's pid to var $pid # tr to change '^@' into ' ' and awk to print 2nt column(script name) name=$(cat -A /proc/$pid/cmdline | tr '^@' ' ' | awk '{print $2}') echo " pid: $pid name: $name " 

result:

$ ./test 1 '2 3' 4 pid: 13091 name: ./test 

Comments

-2

echo "You are running $0"

1 Comment

Is not bash script, is full path.
-8

somthing like this?

export LC_ALL=en_US.UTF-8 #!/bin/bash #!/bin/sh #---------------------------------------------------------------------- start_trash(){ ver="htrash.sh v0.0.4" $TRASH_DIR # url to trash $MY_USER $TRASH_SIZE # Show Trash Folder Size echo "Would you like to empty Trash [y/n]?" read ans if [ $ans = y -o $ans = Y -o $ans = yes -o $ans = Yes -o $ans = YES ] then echo "'yes'" cd $TRASH_DIR && $EMPTY_TRASH fi if [ $ans = n -o $ans = N -o $ans = no -o $ans = No -o $ans = NO ] then echo "'no'" fi return $TRUE } #----------------------------------------------------------------------- start_help(){ echo "HELP COMMANDS-----------------------------" echo "htest www open a homepage " echo "htest trash empty trash " return $TRUE } #end Help #-----------------------------------------------# homepage="" return $TRUE } #end cpdebtemp # -Case start # if no command line arg given # set val to Unknown if [ -z $1 ] then val="*** Unknown ***" elif [ -n $1 ] then # otherwise make first arg as val val=$1 fi # use case statement to make decision for rental case $val in "trash") start_trash ;; "help") start_help ;; "www") firefox $homepage ;; *) echo "Sorry, I can not get a $val for you!";; esac # Case stop 

1 Comment

-1, does not answer the question (doesn't show how to find the script's name), and is a confusing example in a very buggy script.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.