By Jiaaro


2008-09-12 20:39:56 8 Comments

How do I get the path of the directory in which a Bash script is located, inside that script?

I want to use a Bash script as a launcher for another application. I want to change the working directory to the one where the Bash script is located, so I can operate on the files in that directory, like so:

$ ./application

30 comments

@Geoff Nixon 2013-12-21 17:47:37

I believe I've got this one. I'm late to the party, but I think some will appreciate it being here if they come across this thread. The comments should explain:

#!/bin/sh # dash bash ksh # !zsh (issues). G. Nixon, 12/2013. Public domain.

## 'linkread' or 'fullpath' or (you choose) is a little tool to recursively
## dereference symbolic links (ala 'readlink') until the originating file
## is found. This is effectively the same function provided in stdlib.h as
## 'realpath' and on the command line in GNU 'readlink -f'.

## Neither of these tools, however, are particularly accessible on the many
## systems that do not have the GNU implementation of readlink, nor ship
## with a system compiler (not to mention the requisite knowledge of C).

## This script is written with portability and (to the extent possible, speed)
## in mind, hence the use of printf for echo and case statements where they
## can be substituded for test, though I've had to scale back a bit on that.

## It is (to the best of my knowledge) written in standard POSIX shell, and
## has been tested with bash-as-bin-sh, dash, and ksh93. zsh seems to have
## issues with it, though I'm not sure why; so probably best to avoid for now.

## Particularly useful (in fact, the reason I wrote this) is the fact that
## it can be used within a shell script to find the path of the script itself.
## (I am sure the shell knows this already; but most likely for the sake of
## security it is not made readily available. The implementation of "$0"
## specificies that the $0 must be the location of **last** symbolic link in
## a chain, or wherever it resides in the path.) This can be used for some
## ...interesting things, like self-duplicating and self-modifiying scripts.

## Currently supported are three errors: whether the file specified exists
## (ala ENOENT), whether its target exists/is accessible; and the special
## case of when a sybolic link references itself "foo -> foo": a common error
## for beginners, since 'ln' does not produce an error if the order of link
## and target are reversed on the command line. (See POSIX signal ELOOP.)

## It would probably be rather simple to write to use this as a basis for
## a pure shell implementation of the 'symlinks' util included with Linux.

## As an aside, the amount of code below **completely** belies the amount
## effort it took to get this right -- but I guess that's coding for you.

##===-------------------------------------------------------------------===##

for argv; do :; done # Last parameter on command line, for options parsing.

## Error messages. Use functions so that we can sub in when the error occurs.

recurses(){ printf "Self-referential:\n\t$argv ->\n\t$argv\n" ;}
dangling(){ printf "Broken symlink:\n\t$argv ->\n\t"$(readlink "$argv")"\n" ;}
errnoent(){ printf "No such file: "[email protected]"\n" ;} # Borrow a horrible signal name.

# Probably best not to install as 'pathfull', if you can avoid it.

pathfull(){ cd "$(dirname "[email protected]")"; link="$(readlink "$(basename "[email protected]")")"

## 'test and 'ls' report different status for bad symlinks, so we use this.

 if [ ! -e "[email protected]" ]; then if $(ls -d "[email protected]" 2>/dev/null) 2>/dev/null;  then
    errnoent 1>&2; exit 1; elif [ ! -e "[email protected]" -a "$link" = "[email protected]" ];   then
    recurses 1>&2; exit 1; elif [ ! -e "[email protected]" ] && [ ! -z "$link" ]; then
    dangling 1>&2; exit 1; fi
 fi

## Not a link, but there might be one in the path, so 'cd' and 'pwd'.

 if [ -z "$link" ]; then if [ "$(dirname "[email protected]" | cut -c1)" = '/' ]; then
   printf "[email protected]\n"; exit 0; else printf "$(pwd)/$(basename "[email protected]")\n"; fi; exit 0
 fi

## Walk the symlinks back to the origin. Calls itself recursivly as needed.

 while [ "$link" ]; do
   cd "$(dirname "$link")"; newlink="$(readlink "$(basename "$link")")"
   case "$newlink" in
    "$link") dangling 1>&2 && exit 1                                       ;;
         '') printf "$(pwd)/$(basename "$link")\n"; exit 0                 ;;
          *) link="$newlink" && pathfull "$link"                           ;;
   esac
 done
 printf "$(pwd)/$(basename "$newlink")\n"
}

## Demo. Install somewhere deep in the filesystem, then symlink somewhere 
## else, symlink again (maybe with a different name) elsewhere, and link
## back into the directory you started in (or something.) The absolute path
## of the script will always be reported in the usage, along with "$0".

if [ -z "$argv" ]; then scriptname="$(pathfull "$0")"

# Yay ANSI l33t codes! Fancy.
 printf "\n\033[3mfrom/as: \033[4m$0\033[0m\n\n\033[1mUSAGE:\033[0m   "
 printf "\033[4m$scriptname\033[24m [ link | file | dir ]\n\n         "
 printf "Recursive readlink for the authoritative file, symlink after "
 printf "symlink.\n\n\n         \033[4m$scriptname\033[24m\n\n        "
 printf " From within an invocation of a script, locate the script's "
 printf "own file\n         (no matter where it has been linked or "
 printf "from where it is being called).\n\n"

else pathfull "[email protected]"
fi

@user1338062 2014-05-28 07:16:09

For systems having GNU coreutils readlink (eg. linux):

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

There's no need to use BASH_SOURCE when $0 contains the script filename.

@osirisgothra 2015-02-05 00:07:46

unless the script was sourced with . or 'source' in which case it will still be whatever script sourced it, or, if from the command line, '-bash' (tty login) or 'bash' (invoked via 'bash -l') or '/bin/bash' (invoked as an interactive non-login shell)

@user1338062 2018-05-13 04:35:07

I added second pair of quotes around dirname call. Needed if the directory path contains spaces.

@hurrymaplelad 2011-09-16 19:05:51

$_ 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.

@clacke 2014-01-31 14:55:13

It breaks if you source or . the script. In those situations, $_ would contain the last parameter of the last command you ran before the .. $BASH_SOURCE works every time.

@Atul 2019-06-20 22:08:51

The shortest and most elegant way to do this is:

#!/bin/bash
DIRECTORY=$(cd `dirname $0` && pwd)
echo $DIRECTORY

This would work on all platforms and is super clean.

More details can be found in "Which directory is that bash script in?".

@ruuter 2019-09-26 08:43:04

great clean solution, but this will not work if the file is symlinked.

@Fuwjax 2010-04-13 22:12:31

This is a slight revision to the solution e-satis and 3bcdnlklvc04a pointed out in their answer:

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

This should still work in all the cases they listed.

This will prevent popd after a failed pushd, thanks to konsolebox.

@Jay Taylor 2010-06-23 20:32:50

This works perfectly to get the "real" dirname, rather than just the name of a symlink. Thank you!

@konsolebox 2014-07-03 04:15:43

Better SCRIPT_DIR=''; pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && { SCRIPT_DIR=$PWD; popd > /dev/null; }

@Fuwjax 2015-01-19 20:03:31

@konsolebox, what are you trying to defend against? I'm generally a fan of inlining logical conditionals, but what was the specific error that you were seeing in the pushd? I'd match rather find a way to handle it directly instead of returning an empty SCRIPT_DIR.

@konsolebox 2015-01-20 19:21:28

@Fuwjax Natural practice to avoid doing popd in cases (even when rare) where pushd fails. And in case pushd fails, what do you think should be the value of SCRIPT_DIR? The action may vary depending on what may seem logical or what one user could prefer but certainly, doing popd is wrong.

@Mike Bethany 2010-09-09 07:19:22

I tried all of these and none 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 they 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"`"

@reinierpost 2014-03-28 13:12:03

Doesn't work if the script is being sourced from another script.

@Jim 2008-09-13 01:07:11

I don't think this is as easy as others have made it out to be. pwd doesn't work, as the current directory is not necessarily the directory with the script. $0 doesn't always have the information 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 information. In the second and third, pwd does not work. The only way to get the directory 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 directory, and reference it by its full path. Data shoudn't be in the /usr/bin directory anyway, so this is probably the thing to do.

@Richard Duerr 2015-11-18 18:54:12

If you intend to disprove his comment, PROVE that a script CAN access where it's stored with a code example.

@Simon Rigét 2016-02-12 23:40:21

This should do it:

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

This works with symlinks and spaces in path.

See the man pages for dirname and readlink.

From the comment track it seems not to work with Mac OS. I have no idea why that is. Any suggestions?

@Bruno Negrão Zica 2016-06-14 18:27:08

with your solution, invoking the script like ./script.sh shows . instead of the full directory path

@Denis The Menace 2016-11-21 09:55:33

There's no -f option for readlink on MacOS. Use stat instead. But still, it shows . if you are in 'this' dir.

@dragon788 2019-04-22 15:59:00

You need to install coreutils from Homebrew and use greadlink to get the -f option on MacOS because it is *BSD under the covers and not Linux.

@Mr Shark 2008-09-12 20:50:55

You can use $BASH_SOURCE:

#!/bin/bash

scriptdir=`dirname "$BASH_SOURCE"`

Note that you need to use #!/bin/bash and not #!/bin/sh since it's a Bash extension.

@Till 2010-10-25 17:06:20

When I do ./foo/script, then $(dirname $BASH_SOURCE) is ./foo.

@purushothaman poovai 2019-12-17 05:17:43

@Till, In this case we can use realpath command to get full path of ./foo/script. So dirname $(realpath ./foo/script) will give the path of script.

@phatblat 2009-09-26 20:38:02

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 its 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

@T.L 2012-01-11 09:14:04

readlink will not availabe in some platform in default installation. Try to avoid using it if you can

@Catskul 2013-09-17 19:40:27

be careful to quote everything to avoid whitespace issues: export SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"

@cucu8 2014-11-26 09:29:32

In OSX Yosemite 10.10.1 -f is not recognised as an option to readlink. Using stat -f instead does the job. Thanks

@robert 2016-01-14 20:16:28

In OSX, there is greadlink, which is basically the readlink we are all familiar. Here is a platform independent version: dir=`greadlink -f ${BASH_SOURCE[0]} || readlink -f ${BASH_SOURCE[0]}`

@phatblat 2016-01-15 21:27:26

Good call, @robert. FYI, greadlink can easily be installed through homebrew: brew install coreutils

@svvac 2016-06-30 16:52:43

Note that $0 doesn't work if the file is sourced. You get -bash instead of the script name.

@Dave Stern 2017-01-25 17:41:12

In OSX, you can use realpath or grealpath, which will give you the resolved path from the root of the file system. Example: cd /usr/bin; realpath ls /usr/bin/ls

@Kidburla 2018-01-24 12:09:10

@T.L readlink is part of the coreutils package which contains the base GNU utilities. No idea why it wouldn't be available on a GNU-based system. I think we can safely assume OP is using GNU, since the question is about GNU Bash?

@Alexar 2018-04-07 00:33:43

$(dirname $(readlink -f $0)) was my answer, thank you so much!

@Domi 2019-12-26 08:34:40

Python was mentioned a few times. Here is the JavaScript (i.e. node) alternative:

baseDirRelative=$(dirname "$0")
baseDir=$(node -e "console.log(require('path').resolve('$baseDirRelative'))") # get absolute path using node

echo $baseDir

@Alexander Stohr 2019-10-25 16:32:24

disclaimer - the top response does not work in all cases... that's why posting this alternate answer.

as i had problems with the BASH_SOURCE with included 'cd' approach on some very fresh and also on less fresh installed Ubuntu Xenial (16.04) systems when invoking the shell script by means of "sh my_script.sh" i tried out something different that as of now seems to run quite smoothly for my purposes. The approach is a bit more compact in the script and is further much lesser cryptic feeling.

this alternate approach uses the external applications 'realpath' and ''dirname from the coreutils package. (okay, not anyone likes the overhead of invoking secondary processe - but when seeing the multi-line scripting for resolving the true object it wont be that bad either having it solve in a single binary usage.)

so lets see one example of those alternate solution for the described task of querying the true absolute path to the a certain file:

SCRIPT=`realpath -s $0`
SCRIPTPATH=`dirname $SCRIPT`

Or when having the chance of using paths with spaces (or maybe other special chars):

SCRIPT=`realpath -s "$0"`
SCRIPTPATH=`dirname "$SCRIPT"`

indeed, if you dont need the value of the SCRIPT variable then you might be able to merge this two-liner into even a single line. but why really shall you spend the effort for this?

@neu242 2019-12-04 13:08:53

This question is bash specific. If you invoke a script with sh, the shell might be something else, such as zsh or dash.

@Brad Parks 2019-10-08 12:37:16

The following will return the current directory of the script

  • works if it's sourced, or not sourced
  • works if run in the current directory, or some other directory.
  • works if relative directories are used.
  • works with bash, not sure of other shells.
/tmp/a/b/c $ . ./test.sh
/tmp/a/b/c

/tmp/a/b/c $ . /tmp/a/b/c/test.sh
/tmp/a/b/c

/tmp/a/b/c $ ./test.sh
/tmp/a/b/c

/tmp/a/b/c $ /tmp/a/b/c/test.sh
/tmp/a/b/c

/tmp/a/b/c $ cd

~ $ . /tmp/a/b/c/test.sh
/tmp/a/b/c

~ $ . ../../tmp/a/b/c/test.sh
/tmp/a/b/c

~ $ /tmp/a/b/c/test.sh
/tmp/a/b/c

~ $ ../../tmp/a/b/c/test.sh
/tmp/a/b/c

test.sh

#!/usr/bin/env bash

# snagged from: https://stackoverflow.com/a/51264222/26510
function toAbsPath {
    local target
    target="$1"

    if [ "$target" == "." ]; then
        echo "$(pwd)"
    elif [ "$target" == ".." ]; then
        echo "$(dirname "$(pwd)")"
    else
        echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")"
    fi
}

function getScriptDir(){
  local SOURCED
  local RESULT
  (return 0 2>/dev/null) && SOURCED=1 || SOURCED=0

  if [ "$SOURCED" == "1" ]
  then
    RESULT=$(dirname "$1")
  else
    RESULT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
  fi
  toAbsPath "$RESULT"
}

SCRIPT_DIR=$(getScriptDir "$0")
echo "$SCRIPT_DIR"

@MatteoBee 2019-08-27 00:31:07

This is what I crafted throughout the years to use as a header on my bash scripts:

## BASE BRAIN - Get where you're from and who you are.
MYPID=$$
ORIGINAL_DIR="$(pwd)" # This is not a hot air balloon ride..
fa="$0" # First Assumption
ta= # Temporary Assumption
wa= # Weighed Assumption
while true; do
    [ "${fa:0:1}" = "/" ] && wa=$0 && break
    [ "${fa:0:2}" = "./" ] && ta="${ORIGINAL_DIR}/${fa:2}" && [ -e "$ta" ] && wa="$ta" && break
    ta="${ORIGINAL_DIR}/${fa}" && [ -e "$ta" ] && wa="$ta" && break
done
SW="$wa"
SWDIR="$(dirname "$wa")"
SWBIN="$(basename "$wa")"
unset ta fa wa
( [ ! -e "$SWDIR/$SWBIN" ] || [ -z "$SW" ] ) && echo "I could not find my way around :( possible bug in the TOP script" && exit 1

at this point your variables SW SWDIR and SWBIN contain what you need.

@Matheus Lozano 2019-08-26 15:01:25

You can do that just combining the script name ($0) with realpath and/or dirname. It works for Bash and Shell.

#!/usr/bin/env bash

RELATIVE_PATH="${0}"
RELATIVE_DIR_PATH="$(dirname "${0}")"
FULL_DIR_PATH="$(realpath "${0}" | xargs dirname)"
FULL_PATH="$(realpath "${0}")"

echo "RELATIVE_PATH->${RELATIVE_PATH}<-"
echo "RELATIVE_DIR_PATH->${RELATIVE_DIR_PATH}<-"
echo "FULL_DIR_PATH->${FULL_DIR_PATH}<-"
echo "FULL_PATH->${FULL_PATH}<-"

The output will be something like this:

# RELATIVE_PATH->./bin/startup.sh<-
# RELATIVE_DIR_PATH->./bin<-
# FULL_DIR_PATH->/opt/my_app/bin<-
# FULL_PATH->/opt/my_app/bin/startup.sh<-

$0 is the name of the script itself

https://www.tldp.org/LDP/abs/html/othertypesv.html

An example: https://gist.github.com/LozanoMatheus/da96b4e44b89b13ad4af10ac4602ad99

@bestOfSong 2019-08-15 05:53:26

I want to comment on the previous answer up there (https://stackoverflow.com/a/201915/5010054) but don't have enough reputation to do that.

Found a solution for this two years ago on apple's documentation site: https://developer.apple.com/library/archive/documentation/OpenSource/Conceptual/ShellScripting/AdvancedTechniques/AdvancedTechniques.html . And I stuck to this method afterwards. It cannot handle soft link but otherwise works pretty well for me. I'm posting it here for any who needs it and as a request for comment.

#!/bin/sh

# Get an absolute path for the poem.txt file.
POEM="$PWD/../poem.txt"

# Get an absolute path for the script file.
SCRIPT="$(which $0)"
if [ "x$(echo $SCRIPT | grep '^\/')" = "x" ] ; then
    SCRIPT="$PWD/$SCRIPT"
fi

As shown by the code, after you get the absolute path of the script, then you can use dirname command to get the path of the directory.

@kenorb 2013-11-28 12:05:56

Try the following cross-compatible solution:

CWD="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"

as the commands such as realpath or readlink could be not available (depending on the operating system).

Note: In Bash, it's recommended to use${BASH_SOURCE[0]} instead of $0, otherwise path can break when sourcing the file (source/.).

Alternatively you can try the following function in bash:

realpath () {
  [[ $1 = /* ]] && echo "$1" || echo "$PWD/${1#./}"
}

This function takes 1 argument. If argument has already absolute path, print it as it is, otherwise print $PWD variable + filename argument (without ./ prefix).

Related:

@Chris 2015-03-27 16:54:26

Please explain more about the realpath function.

@kenorb 2015-03-27 17:35:01

@Chris realpath function takes 1 argument. If argument has already absolute path, print it as it is, otherwise print $PWD + filename (without ./ prefix).

@Jakub Jirutka 2015-09-08 20:59:09

Your cross-compatible solution doesn’t work when the script is symlinked.

@Andry 2019-05-07 22:06:25

There is no 100% portable and reliable way to request a path to a current script directory. Especially between different backends like cygwin/mingw/msys/Linux and etc. This issue was not properly and completely resolved in the bash for ages.

For example, this could not be resolved if you want to request the path after the source command to make nested inclusion of another bash script which is in turn use the same source command to include another bash script and so on.

In case of source command i suggest to replace the source command to something like this:

function include()
{
  if [[ -n "$CURRENT_SCRIPT_DIR" ]]; then
    local dir_path=... get directory from `CURRENT_SCRIPT_DIR/$1`, depends if $1 is absolute path or relative ...
    local include_file_path=...
  else
    local dir_path=... request the directory from the "$1" argument using one of answered here methods...
    local include_file_path=...
  fi
  ... push $CURRENT_SCRIPT_DIR in to stack ... 
  export CURRENT_SCRIPT_DIR=... export current script directory using $dir_path ...
  source "$include_file_path"
  ... pop $CURRENT_SCRIPT_DIR from stack ...
}

From now and on the use of include(...) is based on previous CURRENT_SCRIPT_DIR in your script.

This only works when you can replace all source commands by include command. If you can't, then you have no choice. At least until developers of the bash interpreter make an explicit command to request the current running script directory path.

My own closest implementation to this: https://sourceforge.net/p/tacklelib/tacklelib/175/tree/trunk/_scripts/bash_entry#l270

@Chaim Leib Halbert 2019-05-22 19:57:49

Here's a command that works under either bash or zsh, and whether executed standalone or sourced:

[ -n "$ZSH_VERSION" ] && this_dir=$(dirname "${(%):-%x}") \
    || this_dir=$(dirname "${BASH_SOURCE[0]:-$0}")

How it works

The zsh current file expansion: ${(%):-%x}

${(%):-%x} in zsh expands to the path of the currently-executing file.

The fallback substitution operator :-

You know already that ${...} substitutes variables inside of strings. You might not know that certain operations are possible (in both bash and zsh) on the variables during substitution, like the fallback expansion operator :-:

% x=ok
% echo "${x}"
ok

% echo "${x:-fallback}"
ok

% x=
% echo "${x:-fallback}"
fallback

% y=yvalue
% echo "${x:-$y}"
yvalue

The %x prompt escape code

Next, we'll introduce prompt escape codes, a zsh-only feature. In zsh, %x will expand to the path of the file, but normally this is only when doing expansion for prompt strings. To enable those codes in our substitution, we can add a (%) flag before the variable name:

% cat apath/test.sh
fpath=%x
echo "${(%)fpath}"

% source apath/test.sh
apath/test.sh

% cd apath
% source test.sh
test.sh

An unlikely match: the percent escape and the fallback

What we have so far works, but it would be tidier to avoid creating the extra fpath variable. Instead of putting %x in fpath, we can use :- and put %x in the fallback string:

% cat test.sh
echo "${(%):-%x}"

% source test.sh
test.sh

Note that we normally would put a variable name between (%) and :-, but we left it blank. The variable with a blank name can't be declared or set, so the fallback is always triggered.

Finishing up: what about print -P %x?

Now we almost have the directory of our script. We could have used print -P %x to get the same file path with fewer hacks, but in our case, where we need to pass it as an argument to dirname, that would have required the overhead of a starting a new subshell:

% cat apath/test.sh
dirname "$(print -P %x)"  # $(...) runs a command in a new process
dirname "${(%):-%x}"

% source apath/test.sh
apath
apath

It turns out that the hacky way is both more performant and succinct.

@cdonat 2019-05-17 12:09:27

This is a pretty old question, but I'll add my answer anyway. I usually use

dirname $(which $BASH_SOURCE)

@Top-Master 2019-03-26 07:50:18

Below stores the script's directory path in dir variable

(also it tries to support being executed in Cygwin using Windows php)

and at last it runs the my-sample-app executable with all arguments passed to this script using "[email protected]"

#!/usr/bin/env sh

dir=$(cd "${0%[/\\]*}" > /dev/null && pwd)

if [ -d /proc/cygdrive ]; then
    case $(which php) in
        $(readlink -n /proc/cygdrive)/*)
            # We are in Cygwin using Windows php, so the path must be translated
            dir=$(cygpath -m "$dir");
            ;;
    esac
fi

# Runs the executable which is beside this script
"${dir}/my-sample-app" "[email protected]"

@Louis Moore 2019-02-06 11:06:02

Another option to compliment all the other excellent answers

$(cd "$(dirname "${BASH_SOURCE[0]}")"; pwd)"

@Keith M 2019-02-07 19:19:11

This has no functional advantage over alanwj's answer, which is more robust, and was posted long before this one. The accepted community wiki answer also includes this solution already.

@danemacmillan 2019-01-10 16:33:48

The chosen answer works very well. I'm posting my solution for anyone looking for shorter alternatives that still addresses sourcing, executing, full paths, relative paths, and symlinks. Finally, this will work on MacOS, given that it cannot be assumed that GNU's coreutils' version of readlink is available.

The gotcha is that it's not using Bash, but is easy to use in a Bash script. While OP did not place any constraints on the language of the solution, it's probably best that most have stayed within the Bash world. This is just an alternative, and possibly an unpopular one.

PHP is available on MacOS by default, and installed on a number of other platforms, though not necessarily by default. I realize this is a shortcoming, but I'll leave this here for any people coming from search engines, anyway.

export SOURCE_DIRECTORY="$(php -r 'echo dirname(realpath($argv[1]));' -- "${BASH_SOURCE[0]}")"

@DmitryBorodin 2019-02-24 22:14:20

There was nothing about MacOS in the question. I think this answer should start with "Another approach is to use PHP instead of relying on BASH". Because this reveals only at the end of the answer.

@dogbane 2008-10-29 08:36:45

#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && 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.

It 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 also want to resolve any links to the script itself, you need a multi-line solution:

#!/bin/bash

SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
  DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && 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" )" >/dev/null 2>&1 && 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, and stderr output side effects if the user has smartly overridden cd to redirect output to stderr instead (including escape sequences, such as when calling update_terminal_cwd >&2 on Mac). Adding >/dev/null 2>&1 at the end of your cd command will take care of both possibilities.

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 [[ $TARGET == /* ]]; 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" )" >/dev/null 2>&1 && 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'

@Jim Garrison 2011-08-09 16:07:27

This also does not work if the shell is invoked with an explicit bash command, as you would need to do if you wanted to invoke with a shell option, as in sh -x myscript.sh

@Dave Dopson 2011-08-16 18:03:08

For one last upgrade, try this: DIR="$( cd -P "$( dirname "$0" )" && pwd )" --- that will give you the absolute dereferenced path, ie, resolves all symlinks.

@eold 2011-08-18 22:00:00

It also does not work if you call the scripts via symbolic link; it returns the directory where the symlink is. But this may even be desirable.

@Dan Moulding 2011-10-19 15:54:43

You can fuse this approach with the answer by user25866 to arrive at a solution that works with source <script> and bash <script>: DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)".

@beltorak 2011-12-15 09:47:26

let me try that again... The while loop breaks if any symlink in the chain is a relative symlink. Consider the following setup: /bin/bar -> /etc/alternatives/bar -> /usr/lib/bar-bsd -> bsdbar You'll need to change the while loop to while [ -h "$SOURCE" ] ; do LAST_SOURCE="$SOURCE"; SOURCE="$(readlink "$SOURCE")"; if [ "${SOURCE:0:1}" != / ] ; then SOURCE="$(dirname "$LAST_SOURCE")/$SOURCE"; fi; done

@rsaw 2012-03-08 18:54:04

When doing variable assignment to the output of command substitution or to the output of a variable reference, there is no reason whatsoever to use quotes. Proof: run x="my name is ryran" and then a=$x or a=${x/ryran/bob jones} or a=$(echo $x)

@fileoffset 2012-03-26 03:50:28

This is great, however I ran into a really weird problem. I have a funky command prompt defined, and when using this exact method I was getting unprintable characters in my DIR. I fixed it by using the following: DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

@Joseph Wright 2012-04-15 19:35:06

To deal with a relative symlink, I found I needed DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd -P "$( dirname "$SOURCE" )" && pwd )"

@dubiousjim 2012-05-31 17:21:15

@ryran: yes, but beware that you cannot portably expect local a=$x or export a=$x to work the same way. I'm not sure about a=$x foo, where foo is some command or builtin, but I suspect that you can't portably rely on the assignment behavior you describe there either.

@rsaw 2012-05-31 23:48:53

@dubiousjim: Hmm. What do you mean "portably expect"? I don't see how you'd have any trouble with any of it -- local/export, whatever. It all works in every version of BASH (and we're only talking about BASH here). As far as "a=$x foo where foo is some command or builtin" ... I'm not sure what you're trying to say there.

@dubiousjim 2012-06-01 00:15:18

@ryran: By "portably" I did mean in other shells. Granted this discussion is bash-specific, but it's worth being aware of what idioms one is using that may unexpectedly break in dash, ash, etc. It's easy to remember that [[ ]] is non-portable; more suprising that local a=$x with no quotes around the $x is too. I just checked FreeBSD sh (a species of ash): here local a=$x only assigns the first word of $x's expansion to a; similarly with export. But a=$x ./foo will (temporarily) assign the whole contents of $x's expansion to a.

@rsaw 2012-06-01 23:36:22

@dubiousjim: I hear you and you're right that it's good to be aware. I don't care though, because I don't ever code shell scripts in anything other than bash since virtually every linux distro comes with gnu bash installed. (I don't mean as the default shell; I just mean installed and available for bash scripts.)

@Qwertie 2012-06-20 21:59:29

Short version does not work in cygwin when the path is like "C:\...". Output of cd is : No such file or directoryabc where abc is the end of the directory name that contains the script. Second version works though.

@weynhamz 2012-12-25 12:13:16

Do not cd to other directory before this code, otherwise it will be wrong.

@JeffCharter 2013-01-11 23:12:38

Note that BASH_SOURCE was added for debugging purposes in version bash-3.0-alpha. So if you are working with a legacy system this won't work.

@user716468 2013-02-03 02:33:52

Sometimes cd prints something to STDOUT! E.g., if your $CDPATH has .. To cover this case, use DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"

@Eonil 2013-04-03 09:41:13

Note that dirname $0 appriach won't work on just started bash shell session. (Of course this is not the case of OP) Try it on just launched terminal. In contrast, BASH_SOURCE approach works well.

@Chris Quenelle 2013-04-17 16:58:04

@fileoffset: You probably have CDPATH set to something. If that variable is set in your environment, the cd command will echo the new directory to stdout. Any portable ksh or bash script that uses cd should either redirect its output, or should prefix the cd command with CDPATH=.

@Chris Quenelle 2013-04-17 16:59:27

Is BASH_SOURCE[0] always the same as $0? If that is true, then why not use $0? It's simpler, more portable and more idiomatic. BASH_SOURCE is useful for processing as a list to find the call stack. In this case, $0 is good enough.

@Matt Kantor 2013-04-28 20:14:11

@Aaron Digulla 2013-11-04 13:40:40

@DaveDopson: I suggest to add >/dev/null after the cd because the above breaks in odd ways when cd prints something to stdout.

@kevinarpe 2013-11-24 07:19:27

In my experience, it is not necessary to add quotes around $() expressions. Thus: FILES=$(ls -1) and FILES="$(ls -1)" are exactly the same.

@Zitrax 2014-02-17 14:40:46

A stupid mistake, but at first I didn't get why ${BASH_SOURCE[0]} didn't work until I realized I had used #!/bin/sh instead of #!/bin/bash.

@x-yuri 2014-04-29 15:18:51

Why not use the following command? DIR=$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")

@jpmc26 2014-08-14 15:13:30

According to ss64's docs, pwd takes a -P argument. The description states that the -P argument ensures the path contains no symbolic links. Could this argument be used to follow symbolic links instead of the loop?

@slm 2014-09-02 19:52:53

@x-yuri - FYI, readlink is not available in the current form on OSX, hence this method would be best, if you're trying to write a semi-crossplatform script on Linux & OSX using Bash.

@lpapp 2014-10-20 17:09:41

Sadly, this will not work from ~/.bashrc because the $DIR variable is only evaluated once at login time, not dynamically all the time. :-(

@jpmc26 2014-12-03 04:00:58

@lpapp If you're trying to reference the directory of ~/.bashrc, you already know it! It's ~. ;) Normally, I don't like to assume, but in this case, it's a pretty safe assumption since it's happening at log in and bash is only looking in that specific place.

@Pete 2015-05-06 12:02:02

This solution doesn't work for autoenv .env scripts (or similar scripts that execute when you cd because it will call itself recursively) why not just DIR=$( dirname "${BASH_SOURCE[0]}" )

@tvlooy 2015-06-09 19:32:10

This accepted answer is not ok, it doesn't work with symlinks and is overly complex. dirname $(readlink -f $0) is the right command. See gist.github.com/tvlooy/cbfbdb111a4ebad8b93e for a testcase

@Adrian Günter 2015-10-28 23:38:56

@tvlooy IMO your answer isn't exactly OK as-is either, because it fails when there is a space in the path. In contrast to a newline character, this isn't unlikely or even uncommon. dirname "$(readlink -f "$0")" doesn't add complexity and is fair measure more robust for the minimal amount of trouble.

@gouessej 2015-10-29 10:39:54

This solution doesn't work in a script called when running an application by clicking on an icon under Ubuntu, the script is mentioned in the Exec attribute. The same script works flawlessly in the terminal. I'm currently investigating. I don't want to set the Path attribute in the desktop file as it wouldn't be distro-agnostic.

@Rany Albeg Wein 2016-01-16 14:02:54

As a side note: By convention, environment variables (PATH, EDITOR, SHELL, ...) and internal shell variables (BASH_VERSION, RANDOM, ...) are fully capitalized. All other variable names should be lowercase. Since variable names are case-sensitive, this convention avoids accidentally overriding environmental and internal variables.

@Rany Albeg Wein 2016-01-30 02:16:59

By convention, environment variables (PATH, EDITOR, SHELL, ...) and internal shell variables (BASH_VERSION, RANDOM, ...) are fully capitalized. All other variable names should be lowercase. Since variable names are case-sensitive, this convention avoids accidentally overriding environmental and internal variables.

@Jesin 2016-09-06 00:29:24

To deal with symlinks, use pwd -P

@smancill 2017-03-20 03:23:38

@switch87 your solution doesn't work in all cases. Please do not edit the answer if you are not 100% sure.

@Andreas 2017-04-27 14:27:16

The oneliner assumes 'cd' is silent. If not you will end up with garbage. Better: DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >>/dev/null && pwd )"

@Alexander Ljungberg 2017-05-09 16:14:06

@tvlooy your comment is not macOS (or probably BSD in general) compatible, while the accepted answer is. readlink -f $0 gives readlink: illegal option -- f.

@tvlooy 2017-05-09 20:31:53

All BSD's have it. OSX doesn't. Install coreutils and use greadline

@zored 2017-07-17 11:00:22

Cute format: DIR=$(cd $(dirname ${BASH_SOURCE[0]}) && pwd)

@tishma 2017-09-09 08:47:30

Seems like no one has the problem with changing working directory without putting it back where it was. :( EDIT: My bad $() creates a new shell, doesn't it?

@Novice C 2017-09-27 12:26:21

While both -h and -L opts for test find wide dual support, there is a preference to favor -L. See these stackoverflow questions: ref 1 and ref 2. However, your milage may vary depending on who is refusing to follow standards.

@Ethan Reesor 2017-11-08 05:13:54

So, @tvlooy, if I'm running on macOS, your solution is to install a software package so I can use your answer? That's far worse than the little bit of complexity of the accepted answer (assuming no symlinks).

@Vladimir Panteleev 2017-12-12 21:31:16

Why not use realpath (as in the other answers) instead of the while loop?

@muni764 2018-08-21 14:16:39

Probably it's not robust, for the link and no link aproach I do the following: LINK=readlink "${BASH_SOURCE[0]}" if [ $? -eq 0 ]; then echo "Link detected" export SCRIPT_DIR="$( cd "$( dirname "$LINK" )" && pwd )" else echo "No link" export SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" fi

@Gabriel Staples 2018-12-02 05:41:52

Note: don't forget the #!/bin/bash at the top. #!/bin/sh won't work. You'll get a Bad substitution error.

@Rich Kuzsma 2019-05-09 15:13:53

The one-liner fails on OSX El Capitan, because of Bash Sessions, which pollute the output with Saving session... ...copying shared history... ...saving history...truncating history files... ...completed. This works: read DIR < <(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)

@wieczorek1990 2019-06-28 09:20:26

I always change the formatting of this line into: DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"

@Alexander - Reinstate Monica 2019-07-12 21:27:23

Is it possible to wrap this in a function, or does it have to be copy/pasted all over the place?

@Andrei LED 2019-08-27 11:49:28

I just found out that the above does not work when the script is straight out executed rather than sourced: stackoverflow.com/questions/27880087/…

@Neil McGill 2019-09-24 13:04:54

@Andrei LED, this seems to work for the bash bug case: $(cd $(dirname ${BASH_SOURCE[@]}) &>/dev/null && pwd)

@Nam G VU 2019-11-22 15:57:51

why ${BASH_SOURCE[0]} instead of simpler $BASH_SOURCE

@Thamme Gowda 2018-11-07 04:30:59

I am tired of coming to this page over and over to copy paste the one-liner in the accepted answer. The problem with that is it is not easy to understand and remember.

Here is an easy-to-remember script:

DIR=$(dirname "${BASH_SOURCE[0]}")  # get the directory name
DIR=$(realpath "${DIR}")    # resolve its full path if need be

@agc 2019-04-04 11:55:34

Or, more obscurely, on one line: DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")

@Alexander Mills 2018-06-01 02:52:46

IF YOUR BASH SCRIPT IS A SYMLINK, then this is the way to do it

#!/usr/bin/env bash

dirn="$(dirname "$0")"
rl="$(readlink "$0")";
exec_dir="$(dirname $(dirname "$rl"))";
my_path="$dirn/$exec_dir";
X="$(cd $(dirname ${my_path}) && pwd)/$(basename ${my_path})"

X is the directory that contains your bash script (the original file, not the symlink). I swear to God this works and is the only way I know of doing this properly.

@user25866 2008-10-07 16:12:14

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

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.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656

@e-satis 2009-11-29 11:34:04

Nice! Could be made shorter replacing "pushd[...] popd /dev/null" by SCRIPT_PATH=readlink -f $(dirname "${VIRTUAL_ENV}");

@Tomer Gabel 2010-01-26 08:19:46

This is by far the most "stable" version I've seen. Thank you!

@ovanes 2010-08-18 10:08:00

Should not the colon in the line 1 be moved in line two between the if- and then-statements?

@ovanes 2010-08-18 10:16:00

And instead of using pushd ...; would not it be better to use $(cd dirname "${SCRIPT_PATH}" && pwd)? But anyway great script!

@sdaau 2011-08-06 13:43:50

Just to note my use case: I saved the script as ~/getpath; and then made a bash alias: echo "alias getpath='"'GP="getpath" ; cp ~/$GP ./$GP ; source ./$GP ; rm ./$GP'"'" >> ~/.bashrc ... and now I can do, in either shell or script: getpath ; echo $SCRIPT_PATH . Thanks for the script - cheers!

@sdaau 2011-08-06 13:50:36

Ups, shame I missed my edit time window - actually, my alias comment above doesn't work in a script, not even with shopt -s expand_aliases (see Aliases)

@Dan Moulding 2011-10-19 15:55:55

You can fuse this approach with the answer by ddopson to arrive at a solution that is a simple one-liner: DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)".

@Adrian Pronk 2012-11-06 01:15:21

It's dangerous for a script to cd out of its current directory in the hope of cding back again later: The script may not have permission to change directory back to the directory that was current when it was invoked. (Same goes for pushd/popd)

@bren brightwell 2014-06-03 16:48:15

readlink -f is GNU-specific. BSD readlink does not have that option.

@Charles Duffy 2014-06-09 03:37:19

What's with all the unnecessary subshells? ([ ... ]) is less efficient than [ ... ], and there's no advantage taken of the isolation offered in return for that performance hit here.

@TomRoche 2017-10-31 21:22:37

The list of cases handled (Works for all versions) above has one item == when the file it. Can anyone appropriately complete that sentence fragment?

@Vivick 2017-11-09 20:49:01

by far, the best

@mwfearnley 2019-03-29 08:57:22

What's with the long hash at the bottom? Is it a signature of some kind? What does it tell us?

@mwfearnley 2019-03-29 09:13:52

In fact, why is this Copyleft in the first place? Does the author ("user25866") want attribution, despite the answer being a community wiki? I don't think SO is a good place to dump a little snippet and then attach a license to it. To guard ownership and authenticate changes, a Github Gist would be much better.

@test11 2011-07-27 07:40:31

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

@blobmaster 2020-01-08 13:04:31

I prefer, $BASH_SOURCE over $0 , because it's explicit even for readers not well-versed in bash. $(dirname -- "$(readlink -f -- "$BASH_SOURCE")")

@Rawstring 2018-04-17 00:03:22

This is how I work it on my scripts: pathvar="$( cd "$( dirname $0 )" && pwd )" This will tell you which directory the Launcher (current script) is being executed from.

@matt b 2008-09-12 20:49:28

Use dirname "$0":

#!/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.

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

@Darron 2008-10-23 20:15:01

For portability beyond bash, $0 may not always be enough. You may need to substitute "type -p $0" to make this work if the command was found on the path.

@D.Shawley 2010-02-05 12:18:04

@Darron: you can only use type -p if the script is executable. This can also open a subtle hole if the script is executed using bash test2.sh and there is another script with the same name executable somewhere else.

@Joachim Sauer 2010-06-11 12:56:58

@Darron: but since the question is tagged bash and the hash-bang line explicitly mentions /bin/bash I'd say it's pretty safe to depend on bashisms.

@T.J. Crowder 2011-01-23 10:30:43

+1, but the problem with using dirname $0 is that if the directory is the current directory, you'll get .. That's fine unless you're going to change directories in the script and expect to use the path you got from dirname $0 as though it were absolute. To get the absolute path: pushd `dirname $0` > /dev/null, SCRIPTPATH=`pwd`, popd > /dev/null: pastie.org/1489386 (But surely there's a better way to expand that path?)

@matt b 2011-01-24 12:55:08

@T.J. Crowder I'm not sure sure dirname $0 is a problem if you assign it to a variable and then use it to launch a script like $dir/script.sh; I would imagine this is the use case for this type of thing 90% of the time. ./script.sh would work fine.

@T.J. Crowder 2011-01-24 13:00:29

@matt b: As I said, it's fine as long as your script using it doesn't change the current directory. If your script does, then you try to launch ./script.sh and script.sh is meant to be in the same directory as the original, it's going to fail because you're no longer there. See also: stackoverflow.com/questions/4774054/…

@ivan_pozdeev 2012-01-11 09:02:36

$0 gives wrong result in sourced scripts (including bashrc)

@Ehtesh Choudhury 2013-03-14 16:45:38

Cool, this doesn't resolve symlinks, which is exactly what I was looking for.

@Volodymyr M. Lisivka 2013-07-09 15:33:39

@T.J. Crowder > But surely there's a better way to expand that path? Yes, you can use $(readlink -f "$0") to get full path in canonical form with all symlinks resolved. Solves tons of problems.

@Du Song 2013-07-21 05:55:46

NOTE ${BASH_SOURCE[0]} is different from $0 when you include one bash file into another using . or source.

@MageProspero 2013-11-18 20:16:29

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"; [ -h "$DIR/$0" ] && DIR=readlink -n "$DIR/$0"

@reinierpost 2014-03-28 13:08:25

Even with bash, $0 is not always enough. A script may source another script!

@joeytwiddle 2015-01-27 07:04:14

@T.J.Crowder @VolodymyrM.Lisivka you could also use $(realpath "$0") but note that neither readlink nor readpath will work on Mac OSX. stackoverflow.com/questions/3572030/…

@Jay jargot 2016-03-31 08:32:41

@mattb This version failed with directory names that contain special chars and double quote char. e.g. mkdir "$(printf "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26‌​\27\30\31\32\33\34\3‌​5\36\37\40\41\42\43\‌​44\45\46\47testdir" "")"

@Demis 2016-06-01 03:06:36

To get this working on Mac OS, probably because of spaces in the path, I had to enclose the argument with quotes, like so: `dirname "$0"`.

@NecipAllef 2018-10-19 12:24:10

echo "I am at `pwd`/`basename $0`" worked for me

Related Questions

Sponsored Content

35 Answered Questions

[SOLVED] How can I check if a program exists from a Bash script?

  • 2009-02-26 21:52:49
  • gregh
  • 635701 View
  • 2085 Score
  • 35 Answer
  • Tags:   bash

21 Answered Questions

[SOLVED] How do I list all files of a directory?

  • 2010-07-08 19:31:22
  • duhhunjonn
  • 3896475 View
  • 3474 Score
  • 21 Answer
  • Tags:   python directory

36 Answered Questions

[SOLVED] How to check if a directory exists in a Bash shell script?

  • 2008-09-12 20:06:25
  • Grundlefleck
  • 2737455 View
  • 3604 Score
  • 36 Answer
  • Tags:   bash shell unix posix

23 Answered Questions

[SOLVED] How to check if a string contains a substring in Bash

  • 2008-10-23 12:37:31
  • davidsheldon
  • 1945379 View
  • 2411 Score
  • 23 Answer
  • Tags:   string bash substring

25 Answered Questions

[SOLVED] How can I safely create a nested directory?

43 Answered Questions

[SOLVED] How to count all the lines of code in a directory recursively?

  • 2009-08-31 17:42:20
  • user77413
  • 747106 View
  • 1565 Score
  • 43 Answer
  • Tags:   bash shell

33 Answered Questions

[SOLVED] How can I add an empty directory to a Git repository?

  • 2008-09-22 16:41:03
  • Laurie Young
  • 908041 View
  • 4097 Score
  • 33 Answer
  • Tags:   git directory git-add

20 Answered Questions

[SOLVED] Get current directory name (without full path) in a Bash script

  • 2009-09-03 03:11:53
  • Derek Dahmer
  • 645185 View
  • 777 Score
  • 20 Answer
  • Tags:   bash shell

Sponsored Content