Can a Bash script tell what directory it's stored in?
59
How do I get the path of the directory in which a bash script is located FROM that bash script?

For instance, lets say I want to use a bash script as a launcher for another application. I want to change working directory to the one where the bash script is located so I can operate on the files in that directory like so: 
improve this question | comment
Maya Gaylord Created at: 2013-11-13 17:07:20 UTC By Maya Gaylord
None of the current solutions work if there are any newlines at the end of the directory name - They will be stripped by the command substitution. To work around this you can append a non-newline character inside the command substitution - DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)" - and remove it without a command substitution - DIR="${DIR%x}". - Abbie Wilderman
@l0b0 Can you suggest a real world situation where a directory would have a newline at the end? I think that would tend to be rather unhelpful. Typing it in the shell sounds very difficult, and I can't imagine how it would help users understand the purpose of the directory (which I view as the reason for naming). - Oma Barton
@jpmc26 There are two very common situations: Accidents and sabotage. A script shouldn't fail in unpredictable ways just because someone, somewhere, did a mkdir $'\n'. - Kristofer Miller IV
Just FYI: in Windows cmd, it's %~dp0. - Ashly Blanda
30 Answers
0
Very late to the discussion, but try something like this:

function get_realpath() {

if [[ -f "$1" ]]
then
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else
        # file *must* be local
        local tmppwd="$PWD"
    fi
else
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

function get_dirname(){

local realpath="$(get_realpath "$1")"
if (( $? )) # true when non-zero.
then
    return $? # failure
fi
echo "${realpath%/*}"
return 0 # success

}

# Then from the top level:
get_dirname './script.sh'

# Or Within a script:
get_dirname "$0"

# Can even test the outcome!
if (( $? )) # true when non-zero.
then
    exit 1 # failure
fi


These functions and related tools are part of our product that has been made available to the community for free and can be found at GitHub as realpath-lib.  It's simple, clean and well documented (great for learning), pure Bash and has no dependencies. Good for cross-platform use too.  So for the above example, within a script you could simply:

source '/path/to/realpath-lib'

get_dirname "$0"

if (( $? )) # true when non-zero.
then
    exit 1 # failure
fi


That's all!
0
$(dirname $(readlink -f $BASH_SOURCE))

0
short answer:

`dirname $0`


or

$(dirname $0)

0
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"


Is a useful one-liner which will give you the full directory name of the script no matter where it is being called from

These will work as long as the last component of the path used to find the script is not a symlink (directory links are OK). If you want to also resolve any links to the script itself, you need a multi-line solution:

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
  SOURCE="$(readlink "$SOURCE")"
  [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"


This last one will work with any combination of aliases, source, bash -c, symlinks, etc.

Beware: if you cd to a different directory before running this snippet, the result may be incorrect! Also, watch out for $CDPATH gotchas.

To understand how it works, try running this more verbose form:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  TARGET="$(readlink "$SOURCE")"
  if [[ $SOURCE == /* ]]; then
    echo "SOURCE '$SOURCE' is an absolute symlink to '$TARGET'"
    SOURCE="$TARGET"
  else
    DIR="$( dirname "$SOURCE" )"
    echo "SOURCE '$SOURCE' is a relative symlink to '$TARGET' (relative to '$DIR')"
    SOURCE="$DIR/$TARGET" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
  fi
done
echo "SOURCE is '$SOURCE'"
RDIR="$( dirname "$SOURCE" )"
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
if [ "$DIR" != "$RDIR" ]; then
  echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"


And it will print something like:

SOURCE './scriptdir.sh' is a relative symlink to 'sym2/scriptdir.sh' (relative to '.')
SOURCE is './sym2/scriptdir.sh'
DIR './sym2' resolves to '/home/ubuntu/dotfiles/fo fo/real/real1/real2'
DIR is '/home/ubuntu/dotfiles/fo fo/real/real1/real2'

0
Use dirname:

#!/bin/bash
echo "The script you are running has basename `basename $0`, dirname `dirname $0`"
echo "The present working directory is `pwd`"


using pwd alone will not work if you are not running the script from the directory it is contained in.

[matt@server1 ~]$ pwd
/home/matt
[matt@server1 ~]$ ./test2.sh
The script you are running has basename test2.sh, dirname .
The present working directory is /home/matt
[matt@server1 ~]$ cd /tmp
[matt@server1 tmp]$ ~/test2.sh
The script you are running has basename test2.sh, dirname /home/matt
The present working directory is /tmp

0
A slight revision to the solution e-satis and 3bcdnlklvc04a pointed out in their answer

pushd $(dirname $(readlink -f "$BASH_SOURCE")) > /dev/null
SCRIPT_DIR="$PWD"
popd > /dev/null


This should still work in all the cases they listed.
0
I tried every one of these and none of them worked. One was very close but had a tiny bug that broke it badly; they forgot to wrap the path in quotation marks.

Also a lot of people assume you're running the script from a shell so forget when you open a new script it defaults to your home.

Try this directory on for size:

/var/No one/Thought/About Spaces Being/In a Directory/Name/And Here's your file.text

This gets it right regardless how or where you run it.

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename "$0"`"
echo "dirname: `dirname "$0"`"


So to make it actually useful here's how to change to the directory of the running script:

cd "`dirname "$0"`"


Hope that helps
0
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
  while([ -h "${SCRIPT_PATH}" ]) do SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
pushd . > /dev/null
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null


Works for all versions,including                                                                                                              

when called via multple depth soft link,                                                                                                      
when the file it
when script called by command "source" aka . (dot) operator.                                                                                  
when arg $0 is modified from caller.                                                                                                          
"./script" 
"/full/path/to/script" 
"/some/path/../../another/path/script"
"./some/folder/script"                                                                                                                 
Alternatively, if the bash script itself is a relative symlink you want to follow it and return the full path of the linked-to script:

pushd . > /dev/null
SCRIPT_PATH="${BASH_SOURCE[0]}";
if ([ -h "${SCRIPT_PATH}" ]) then
  while([ -h "${SCRIPT_PATH}" ]) do cd `dirname "$SCRIPT_PATH"`; SCRIPT_PATH=`readlink "${SCRIPT_PATH}"`; done
fi
cd `dirname ${SCRIPT_PATH}` > /dev/null
SCRIPT_PATH=`pwd`;
popd  > /dev/null


SCRIPT_PATH is given in full path, no matter how it is called.
Just make sure you locate this at start of the script.

This comment and code Copyleft, selectable license under the GPL2.0 or later or CC-SA 3.0  (CreativeCommons Share Alike) or later. (c) 2008. All rights reserved. No warranty of any kind. You have been warned.http://www.gnu.org/licenses/gpl-2.0.txthttp://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656
0
The dirname command is the most basic, simply parsing the path up to the filename off of the $0 (script name) variable:

dirname "$0"


But, as matt b pointed out, the path returned is different depending on how the script is called. pwd doesn't do the job because that only tells you what the current directory is, not what directory the script resides in. Additionally, if a symbolic link to a script is executed, you're going to get a (probably relative) path to where the link resides, not the actual script. 

Some others have mentioned the readlink command, but at it's simplest, you can use:

dirname "$(readlink -f "$0")"


readlink will resolve the script path to an absolute path from the root of the filesystem. So, any paths containing single or double dots, tildes and/or symbolic links will be resolved to a full path.

Here's a script demonstrating each of these, whatdir.sh:

#!/bin/bash
echo "pwd: `pwd`"
echo "\$0: $0"
echo "basename: `basename $0`"
echo "dirname: `dirname $0`"
echo "dirname/readlink: $(dirname $(readlink -f $0))"


Running this script in my home dir, using a relative path:

>>>$ ./whatdir.sh 
pwd: /Users/phatblat
$0: ./whatdir.sh
basename: whatdir.sh
dirname: .
dirname/readlink: /Users/phatblat


Again, but using the full path to the script:

>>>$ /Users/phatblat/whatdir.sh 
pwd: /Users/phatblat
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat


Now changing directories:

>>>$ cd /tmp
>>>$ ~/whatdir.sh 
pwd: /tmp
$0: /Users/phatblat/whatdir.sh
basename: whatdir.sh
dirname: /Users/phatblat
dirname/readlink: /Users/phatblat


And finally using a symbolic link to execute the script:

>>>$ ln -s ~/whatdir.sh whatdirlink.sh
>>>$ ./whatdirlink.sh 
pwd: /tmp
$0: ./whatdirlink.sh
basename: whatdirlink.sh
dirname: .
dirname/readlink: /Users/phatblat

0
You can use $BASH_SOURCE

#!/bin/bash

scriptdir=`dirname $BASH_SOURCE`


Note that you need to use #!/bin/bash and not #!/bin/sh since its a bash extension
0
It is not possible to find the location reliably in 100% of all cases!

Greg Wooledge ('greycat' on freenode #bash IRC channel) explains this very thoroughly in the Bash FAQ at the GreyCatWiki
0
I don't think this is as easy as others have made it out to be.  pwd doesn't work, as the current dir is not necessarily the directory with the script.  $0 doesn't always have the info either.  Consider the following three ways to invoke a script.

./script

/usr/bin/script

script


In the first and third ways $0 doesn't have the full path info.  In the second and third, pwd do not work.  The only way to get the dir in the third way would be to run through the path and find the file with the correct match.  Basically the code would have to redo what the OS does.

One way to do what you are asking would be to just hardcode the data in the /usr/share dir, and reference it by full path.  Data shoudn't be in the /usr/bin dir anyway, so this is probably the thing to do.
0
pwd can be used to find the current working directory, and dirname to find the directory of a particular file (command that was run, is $0, so dirname $0 should give you the directory of the current script).

However, dirname gives precisely the directory portion of the filename, which more likely then not is going to be relative to the current working directory. If your script needs to change directory for some reason, then the output from dirname becomes meaningless.

I suggest the following:

#!/bin/bash

reldir=`dirname $0`
cd $reldir
directory=`pwd`

echo "Directory is $directory"


This way, you get an absolute, rather then relative directory.

Since the script will be run in a seperate bash instance, there is no need to restore the working directory afterwards, but if you do want to change back in your script for some reason, you can easily assign the value of pwd to a variable before you change directory, for future use.

Although just

cd `dirname $0`


solves the specific scenario in the question, I find having the absolute path to more more useful generally.
0
This is linux specific, but you could use:

SELF=$(readlink /proc/$$/fd/255)

0
Here is a POSIX compliant one-liner:

SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`

# test
echo $SCRIPT_PATH

0
This gets the current working directory on Mac OS X 10.6.6:

DIR=$(cd "$(dirname "$0")"; pwd)

0
Hmm, if in the path basename & dirname are just not going to cut it
and walking the path is hard (what if parent didn't export PATH!).
However, the shell has to have an open handle to its script, and in
bash the handle is #255.

SELF=`readlink /proc/$$/fd/255`


works for me.
0
I would use something like this:

# retrieve the full pathname of the called script
scriptPath=$(which $0)

# check whether the path is a link or not
if [ -L $scriptPath ]; then

    # it is a link then retrieve the target path and get the directory name
    sourceDir=$(dirname $(readlink -f $scriptPath))

else

    # otherwise just get the directory name of the script path
    sourceDir=$(dirname $scriptPath)

fi

0
$_ is worth mentioning as an alternative to $0.  If you're running a script from bash, the accepted answer can be shortened to:

DIR="$( dirname "$_" )"


Note that this has to be the first statement in your script.
0
I've compared many of the answers given, and come up with some more compact solutions. These seem to handle all of the crazy edge cases that arise from your favorite combination of:

Absolute paths or relative paths
File and directory soft links
Invocation as script, bash script, bash -c script, source script, or . script
Spaces, tabs, newlines, unicode, etc. in directories and/or filename
If you're running from Linux, it seems that using the proc handle is the best solution to locate the fully resolved source of the currently running script (in an interactive session, the link points to the respective /dev/pts/X):

RESOLVED="$(readlink /proc/$$/fd/255 && echo X)" && RESOLVED="${RESOLVED%$'\nX'}"


This has a small bit of ugliness to it, but the fix is compact and easy to understand. We aren't using bash primitives only, but I'm okay with that because readlink simplifies the task considerably. The echo X adds an X to the end of the variable string so that any trailing whitespace in the filename doesn't get eaten, and the parameter substitution ${VAR%X} at the end of the line gets rid of the X. Because readlink adds a newline of its own (which would normally be eaten in the command substitution if not for our previous trickery), we have to get rid of that, too. This is most easily accomplished using the $'' quoting scheme, which lets us use escape sequences such as \n to represent newlines (this is also how you can easily make deviously named directories and files).

The above should cover your needs for locating the currently running script on Linux, but if you don't have the proc filesystem at your disposal, or if you're trying to locate the fully resolved path of some other file, then maybe you'll find the below code helpful. It's only a slight modification from the above one-liner. If you're playing around with strange directory/filenames, checking the output with both ls and readlink is informative, as ls will output "simplified" paths, substituting ? for things like newlines.

DIR="$( dirname "$(readlink -e "${BASH_SOURCE[0]}")" && echo X)" && DIR="${DIR%$'\nX'}"
FIL="$(basename "$(readlink -e "${BASH_SOURCE[0]}")" && echo X)" && FIL="${FIL%$'\nX'}"

ls "$DIR/$FIL"
readlink "$DIR/$FIL"

0
SCRIPT_DIR=$( cd ${0%/*} && pwd -P )

0
None of these worked for a bash script launched by Finder in OS X - I ended up using:

SCRIPT_LOC="`ps -p $$ | sed /PID/d | sed s:.*/Network/:/Network/: |
sed s:.*/Volumes/:/Volumes/:`"


Not pretty, but it gets the job done.
0
I want to make sure that the script is running in its directory. So
cd $(dirname $(which $0) )

After this, if you really want to know where the you are running then run the command below.
DIR=$(/usr/bin/pwd)
0
This is the only way I've found to tell reliably:

SCRIPT_DIR=$(dirname $(cd "$(dirname "$BASH_SOURCE")"; pwd))
0
function getScriptAbsoluteDir { # fold>>
    # @description used to get the script path
    # @param $1 the script $0 parameter
    local script_invoke_path="$1"
    local cwd=`pwd`

    # absolute path ? if so, the first character is a /
    if test "x${script_invoke_path:0:1}" = 'x/'
    then
        RESULT=`dirname "$script_invoke_path"`
    else
        RESULT=`dirname "$cwd/$script_invoke_path"`
    fi
} # <<fold

0
This works in bash-3.2:

path="$( dirname "$( which "$0" )" )"


Here's an example of its usage:

Say you have a ~/bin directory, which is in your $PATH. You have script A inside this directory. It source*s script *~/bin/lib/B. You know where the included script is relative to the original one (the subdirectory lib), but not where it is relative to the user's current directory.

This is solved by the following (inside A):

source "$( dirname "$( which "$0" )" )/lib/B"


It doesn't matter where the user is or how he calls the script, this will always work.
0
Use a combination of readlink to canonicalize the name (with a bonus of following it back to its source if it is a symlink) and dirname to extract the directory name:

script="`readlink -f "${BASH_SOURCE[0]}"`"
dir="`dirname "$script"`"

0
Try using:

real=$(realpath $(dirname $0))

0
I usually include the following at the top of my scripts which works in the majority of cases:

[ "$(dirname $0)" = '.' ] && SOURCE_DIR=$(pwd) || SOURCE_DIR=$(dirname $0);
ls -l $0 | grep -q ^l && SOURCE_DIR=$(ls -l $0 | awk '{print $NF}');


The first line assigns source based on the value of pwd if run from current path or dirname if called from elsewhere.

The second line examines the path to see if it is a symlink and if so, updates SOURCE_DIR to the location of the link itself.

There are probably better solutions out there but this is the cleanest I've managed to come up with myself.
0
#!/bin/sh
PRG="$0"

# need this for relative symlinks
while [ -h "$PRG" ] ; do
   PRG=`readlink "$PRG"`
done

scriptdir=`dirname "$PRG"`

Your Answer