Basically I need to run the script with paths related to the shell script file location, how can I change the current directory to the same directory as where the script file resides?
-
14Is that really a duplicate? This question is about a "unix shell script", the other specifically about Bash. – michaeljt Jun 23 '15 at 09:45
-
2@BoltClock: This question was improperly closed. The linked question is about Bash. This question is about Unix shell programming. Notice that the accepted answers are quite different! – Dietrich Epp Jun 16 '16 at 07:56
-
@Dietrich Epp: You're right. It seems the asker's choice of accepted answer and the addition of the [bash] tag (probably in response to that) led me to marking the question as a duplicate in response to a flag. – BoltClock Jun 16 '16 at 08:03
-
3I think this answer is better: [Getting the source directory of a Bash script from within](https://stackoverflow.com/a/246128/2708288) – Alan Zhiliang Feng Jun 02 '17 at 07:50
-
2Possible duplicate of [Getting the source directory of a Bash script from within](https://stackoverflow.com/questions/59895/getting-the-source-directory-of-a-bash-script-from-within) – Dulgan Jul 03 '17 at 13:53
-
1Google brought me here as I was looking for a pure posix implementation of the solution. After a little bit more searching I came across this very detailed answer explaining why it can't be done and a way to work around it by shimming support for popular shells. https://stackoverflow.com/a/29835459/816584 – Steve Buzonas Dec 01 '17 at 17:04
-
Can’t the output of the ‘pwd’ command be assigned to a variable at the start of the script and then use the variable through out the script. Not sure if I got the question correct – abhishek phukan Apr 22 '18 at 12:02
19 Answers
In Bash, you should get what you need like this:
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
-
27This doesn't work if you've called the script via a symbolic link in a different directory. To make that work you need to use `readlink` as well (see al's answer below) – AndrewR Mar 17 '10 at 23:26
-
59In bash it is safer to use `$BASH_SOURCE` in lieu of `$0`, because `$0` doesn't always contain the path of the script being invoked, such as when 'sourcing' a script. – mklement0 Jul 19 '12 at 19:32
-
7`$BASH_SOURCE` is Bash-specific, the question is about shell script in general. – Ha-Duong Nguyen May 27 '14 at 02:59
-
1
-
11@auraham: `CUR_PATH=$(pwd)` or `pwd` do return the current directory (which does not have to be the scripts parent dir)! – Andreas Covidiot Jul 24 '14 at 07:58
-
This would not work with symlinks. Checkout my hopefully more consistent solution here https://github.com/lingtalfi/printScriptDir/blob/master/printScriptDir.sh, it handles symlinks, relative paths, absolute paths, tested on linux and macOsX – ling Oct 02 '15 at 22:09
-
5I tried the method @mklement0 recommended, using `$BASH_SOURCE`, and it returns what I needed. My script is being called from another script, and `$0` returns `.` while `$BASH_SOURCE` returns the right subdirectory (in my case `scripts`). – David Rissato Cruz Dec 03 '15 at 16:28
-
2@BillLynch: That's only partially true: a script in the path that is invoked _directly_, via its shebang line, _does_ contain the script's _full_ path in `$0`. By contrast, if you do `bash
`, it does not - but this method of invocation is unusual and ill-advised (as an aside: Bash and Ksh support this method of invocation, Dash and Zsh do not). Either way, `$BASH_SOURCE` is the right variable to use in Bash. (Also: `$0` should be double-quoted in the answer.) – mklement0 Dec 03 '15 at 18:44 -
1@CecilCurry, yes this is idiotic. This answer is wrong for many reasons. When it works, it is the current directory, not the directory the script file resides. Even then, it doesn't resolve . and ..\ – thang Jun 20 '16 at 19:45
-
How do you solve ".."s in the path? (such as `$PWD/$BASH_SOURCE` == `foo/../bar`) – Jean-Luc Nacif Coelho Mar 20 '17 at 22:02
-
2@thang it does not print current directory, but relative directory of the script. – jarno Jul 02 '17 at 19:50
-
@jamo, you're right. that's what i meant. i just ran it in while in the containing dir and it returns the ./ – thang Jul 06 '17 at 19:53
-
SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" , based on this answer https://stackoverflow.com/questions/4774054 – Happy Jun 28 '18 at 04:55
-
-
this code does not work when current directory is same as current directory of the current script. so this is better > `$(dirname $(realpath $0))` – Think Big Nov 22 '20 at 06:33
-
2@ThinkBig, quotes matter. `"$(dirname "$(realpath "$0")")"` is more correct when you're dealing with names with spaces, or when `IFS` has been changed to a value present in the path. – Charles Duffy Feb 27 '21 at 22:40
-
This is not the best answer (though highly upvoted). If you call the script with a relative path or just with the name, dirname will just be something like "." or "sbin". You may want the absolute path. See other answers. – Sybille Peters Mar 05 '23 at 06:50
The original post contains the solution (ignore the responses, they don't add anything useful). The interesting work is done by the mentioned unix command readlink
with option -f
. Works when the script is called by an absolute as well as by a relative path.
For bash, sh, ksh:
#!/bin/bash
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT=$(readlink -f "$0")
# Absolute path this script is in, thus /home/user/bin
SCRIPTPATH=$(dirname "$SCRIPT")
echo $SCRIPTPATH
For tcsh, csh:
#!/bin/tcsh
# Absolute path to this script, e.g. /home/user/bin/foo.csh
set SCRIPT=`readlink -f "$0"`
# Absolute path this script is in, thus /home/user/bin
set SCRIPTPATH=`dirname "$SCRIPT"`
echo $SCRIPTPATH
See also: https://stackoverflow.com/a/246128/59087
-
This is the best way. Note the 'checked' answer will not work if you do: ./myscript.sh ...that would report that the directory is "." - this will always give you the absolute path – EdH Jan 23 '11 at 21:19
-
14Note: Not all systems have `readlink`. That's why I recommended using pushd/popd (built-ins for bash). – docwhat May 20 '11 at 14:29
-
29The `-f` option to `readlink` does something different on OS X (Lion) and possibly BSD. http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac – Ergwun Jun 29 '12 at 01:33
-
12To clarify @Ergwun's comment: `-f` is not supported on OS X at all (as of Lion); there you can either drop the `-f` to make do with resolving at most one level of indirection, e.g. `pushd "$(dirname "$(readlink "$BASH_SOURCE" || echo "$BASH_SOURCE")")"`, or you can roll your own recursive symlink-following script as demonstrated in the linked post. – mklement0 Jul 19 '12 at 19:37
-
2I still don't understand, why the OP would need the absolute path. Reporting "." should work alright if you want to access files relative to the scripts path and you called the script like ./myscript.sh – Stefan Haberl Mar 12 '14 at 08:25
-
@EdH OP asked for *paths related to the shell script file location*. I take that to mean *relative* path, not *absolute* path. – toxalot Mar 16 '14 at 07:10
-
13@StefanHaberl I think it would be an issue if you ran the script while your present working directory was different from the script's location (e.g. `sh /some/other/directory/script.sh)`, in this case `.` would be your pwd, not `/some/other/directory` – jrz Oct 09 '14 at 11:18
-
1This doesn't work if the script is sourced. In that case $0 will show just a shell name – vladisld Jul 14 '15 at 17:40
-
1combining this information with mklement0's comment to the accepted answer, I came up with `HERE=$(dirname $(readlink -f $BASH_SOURCE))/` I included a trailing slash to prevent mishaps like this `sudo rm-rf ${AccidentallyUndefinedVariable}/` – skiggety Nov 29 '16 at 17:01
-
This is much better than the demarko answer and I got to learn a new function but it's still very buggy. It won't work if you're launching the script from the same dir ($0=./script in this case and readlink won't know what to do with it) and it won't work if you're using launching the script via relative parths (ex. ../scriptfolder/script). I have a fix for that. I'll put it up after I've tested it for a few days. – thebunnyrules Mar 26 '18 at 07:31
An earlier comment on an answer said it, but it is easy to miss among all the other answers.
When using bash:
echo this file: "$BASH_SOURCE"
echo this dir: "$(dirname "$BASH_SOURCE")"
-
3Only this one works with script in environment path, the most voted ones do not work. thank you! – July Sep 22 '16 at 05:20
-
You should use `dirname "$BASH_SOURCE"` instead to handle spaces in $BASH_SOURCE. – Mygod Jan 08 '18 at 12:16
-
1A more explicit way to print the directory would, according to tool ShellCheck, be: "$(dirname "${BASH_SOURCE{0}}")" because BASH_SOURCE is an array, and without the subscript, the first element is taken by default. – Adrian M. Dec 02 '18 at 03:55
-
1@AdrianM. , you want brackets, not braces, for the index: `"$(dirname "${BASH_SOURCE[0]}")"` – Hashbrown Aug 12 '19 at 01:59
-
1No good for me..prints just a dot (i.e. the current directory, presumably) – JL_SO Aug 23 '19 at 12:05
Assuming you're using bash
#!/bin/bash
current_dir=$(pwd)
script_dir=$(dirname "$0")
echo $current_dir
echo $script_dir
This script should print the directory that you're in, and then the directory the script is in. For example, when calling it from /
with the script in /home/mez/
, it outputs
/
/home/mez
Remember, when assigning variables from the output of a command, wrap the command in $(
and )
- or you won't get the desired output.
-
4
-
7
-
1To me, $current_dir is indeed the path I'm calling the script from. However, $script_dir is not the script's dir, it's just a dot. – Michael Feb 25 '20 at 17:06
-
1@Michael The script_dir is relative to current_dir. So if you run the script from the directory it is stored in, you'll simply get a dot in script_dir. – Vaibhav Jain Nov 24 '20 at 04:29
-
`$(pwd)` is a lot of overhead compared to `$PWD`, which [POSIX explicitly requires](https://pubs.opengroup.org/onlinepubs/009696899/basedefs/xbd_chap08.html) to be set on shell startup and on `cd`. – Charles Duffy Feb 27 '21 at 22:41
The best answer for this question was answered here:
Getting the source directory of a Bash script from within
And it is:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
One-liner which will give you the full directory name of the script no matter where it is being called from.
To understand how it works you can execute the following script:
#!/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" )" && pwd )"
if [ "$DIR" != "$RDIR" ]; then
echo "DIR '$RDIR' resolves to '$DIR'"
fi
echo "DIR is '$DIR'"

- 1,291
- 17
- 16
If you're using bash....
#!/bin/bash
pushd $(dirname "${0}") > /dev/null
basedir=$(pwd -L)
# Use "pwd -P" for the path without links. man bash for more info.
popd > /dev/null
echo "${basedir}"

- 11,435
- 6
- 55
- 54
-
4You can replace the `pushd`/`popd` with `cd $(dirname "${0}")` and `cd -` to make it work on other shells, if they have a `pwd -L`. – docwhat May 20 '11 at 14:30
-
-
1So I don't have to store the original directory in a variable. It's a pattern I use a lot in functions and such. It nests really well, which is good. – docwhat Mar 27 '14 at 03:06
-
It is still being stored in memory -- in a variable -- whether a variable is referenced in your script or not. Also, I believe the cost of executing pushd and popd far outweighs the savings of not creating a local Bash variable in your script, both in CPU cycles and readability. – ingyhere Jan 06 '16 at 21:07
If you want to get the actual script directory (irrespective of whether you are invoking the script using a symlink or directly), try:
BASEDIR=$(dirname $(realpath "$0"))
echo "$BASEDIR"
This works on both linux and macOS. I couldn't see anyone here mention about realpath
. Not sure whether there are any drawbacks in this approach.
on macOS, you need to install coreutils
to use realpath
. Eg: brew install coreutils
.

- 1,301
- 3
- 21
- 31
-
This seems to work well on POSIX compliant shells like `ash` and `dash` (e.g. on Alpine Linux) – jchook Oct 21 '21 at 03:43
As theMarko suggests:
BASEDIR=$(dirname $0)
echo $BASEDIR
This works unless you execute the script from the same directory where the script resides, in which case you get a value of '.'
To get around that issue use:
current_dir=$(pwd)
script_dir=$(dirname $0)
if [ $script_dir = '.' ]
then
script_dir="$current_dir"
fi
You can now use the variable current_dir throughout your script to refer to the script directory. However this may still have the symlink issue.

- 257
- 2
- 5
Let's make it a POSIX oneliner:
a="/$0"; a="${a%/*}"; a="${a:-.}"; a="${a##/}/"; BINDIR=$(cd "$a"; pwd)
Tested on many Bourne-compatible shells including the BSD ones.
As far as I know I am the author and I put it into public domain. For more info see: https://www.bublina.eu.org/posts/2017-05-11-posix_shell_dirname_replacement/

- 1,517
- 1
- 16
- 13
-
1as written, `cd: too many arguments` if spaces in the path, and returns `$PWD`. (an obvious fix, but just shows how many edge cases there actually are) – michael Jul 29 '17 at 19:02
-
1Would upvote. Except for the comment from @michael that it fails with spaces in path... Is there a fix for that? – spechter Feb 20 '18 at 04:13
-
2ln -s /home/der/1/test /home/der/2/test && /home/der/2/test => /home/der/2 (should show path to the original script instead) – Der_Meister Oct 23 '18 at 07:41
-
@Der_Meister Thanks for example. I see your point. This can be canonized only using `readlink -f` or `realpath` as far as I know and that would be pretty much out of scope for this tiny POSIX-shell implementation of dirname I think. Or do you have an idea how to fix it using POSIX shell-only commands? – Ján Sáreník Oct 24 '18 at 14:44
-
1I agree with you. Personally I use `readlink` on Linux and `greadlink` on macOS. – Der_Meister Oct 25 '18 at 15:29
-
1
-
-
1@JánSáreník I believe what you want to use is actually `BINDIR=$(cd "$a"; pwd)` instead of `BINDIR="$(cd $a; pwd)"`, since the `cd $a` fails when `$a` has a space, and `$(...)` itself functions as quoted and therefore adding additional quotes around it – i.e.: `"$(...)"` – is redundant. – michael Aug 19 '22 at 04:16
BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"

- 25,269
- 4
- 47
- 72

- 329
- 2
- 5
INTRODUCTION
This answer corrects the very broken but shockingly top voted answer of this thread (written by TheMarko):
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
WHY DOES USING dirname "$0" ON IT'S OWN NOT WORK?
dirname $0 will only work if user launches script in a very specific way. I was able to find several situations where this answer fails and crashes the script.
First of all, let's understand how this answer works. He's getting the script directory by doing
dirname "$0"
$0 represents the first part of the command calling the script (it's basically the inputted command without the arguments:
/some/path/./script argument1 argument2
$0="/some/path/./script"
dirname basically finds the last / in a string and truncates it there. So if you do:
dirname /usr/bin/sha256sum
you'll get: /usr/bin
This example works well because /usr/bin/sha256sum is a properly formatted path but
dirname "/some/path/./script"
wouldn't work well and would give you:
BASENAME="/some/path/." #which would crash your script if you try to use it as a path
Say you're in the same dir as your script and you launch it with this command
./script
$0 in this situation will be ./script and dirname $0 will give:
. #or BASEDIR=".", again this will crash your script
Using:
sh script
Without inputting the full path will also give a BASEDIR="."
Using relative directories:
../some/path/./script
Gives a dirname $0 of:
../some/path/.
If you're in the /some directory and you call the script in this manner (note the absence of / in the beginning, again a relative path):
path/./script.sh
You'll get this value for dirname $0:
path/.
and ./path/./script (another form of the relative path) gives:
./path/.
The only two situations where basedir $0 will work is if the user use sh or touch to launch a script because both will result in $0:
$0=/some/path/script
which will give you a path you can use with dirname.
THE SOLUTION
You'd have account for and detect every one of the above mentioned situations and apply a fix for it if it arises:
#!/bin/bash
#this script will only work in bash, make sure it's installed on your system.
#set to false to not see all the echos
debug=true
if [ "$debug" = true ]; then echo "\$0=$0";fi
#The line below detect script's parent directory. $0 is the part of the launch command that doesn't contain the arguments
BASEDIR=$(dirname "$0") #3 situations will cause dirname $0 to fail: #situation1: user launches script while in script dir ( $0=./script)
#situation2: different dir but ./ is used to launch script (ex. $0=/path_to/./script)
#situation3: different dir but relative path used to launch script
if [ "$debug" = true ]; then echo 'BASEDIR=$(dirname "$0") gives: '"$BASEDIR";fi
if [ "$BASEDIR" = "." ]; then BASEDIR="$(pwd)";fi # fix for situation1
_B2=${BASEDIR:$((${#BASEDIR}-2))}; B_=${BASEDIR::1}; B_2=${BASEDIR::2}; B_3=${BASEDIR::3} # <- bash only
if [ "$_B2" = "/." ]; then BASEDIR=${BASEDIR::$((${#BASEDIR}-1))};fi #fix for situation2 # <- bash only
if [ "$B_" != "/" ]; then #fix for situation3 #<- bash only
if [ "$B_2" = "./" ]; then
#covers ./relative_path/(./)script
if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/${BASEDIR:2}"; else BASEDIR="/${BASEDIR:2}";fi
else
#covers relative_path/(./)script and ../relative_path/(./)script, using ../relative_path fails if current path is a symbolic link
if [ "$(pwd)" != "/" ]; then BASEDIR="$(pwd)/$BASEDIR"; else BASEDIR="/$BASEDIR";fi
fi
fi
if [ "$debug" = true ]; then echo "fixed BASEDIR=$BASEDIR";fi

- 1,520
- 15
- 22
-
1I would say "very broken" is a shocking overstatement. Yeah, you get the path the way that you called the script. What about it? It should still be correct in the context where you called it. Which is probably what you mostly care.. but if you need to have some sort of canonicalised version with absolute path, because you wanted to e.g. append it to `PATH`, then you'd just `cd` to the folder and get `pwd`. But the usual need to get the path I guess is to be able to call other scripts relative to the path of the current, in which I fail to see how any of those cases could cause any issues. – Timo Oct 07 '20 at 07:18
-
...apart from symlinks ofc. Supporting them while resolving the actual location of the script is a bit trickier. But your chief complaints above did not mention symlinks. – Timo Oct 07 '20 at 07:24
This one-liner tells where the shell script is, does not matter if you ran it or if you sourced it. Also, it resolves any symbolic links involved, if that is the case:
dir=$(dirname $(test -L "$BASH_SOURCE" && readlink -f "$BASH_SOURCE" || echo "$BASH_SOURCE"))
By the way, I suppose you are using /bin/bash.

- 5,675
- 2
- 44
- 50
Basic version:
dir=$(dirname $0)
If the script might be called via $PATH
, then:
dir=$(dirname $(which $0))
If the script might be called like this: bash script.sh
, then:
dir=$(dirname $(which $0 2>/dev/null || realpath $0))
If you feel insanely unsafe, then:
dir="$(dirname -- "$(which -- "$0" 2>/dev/null || realpath -- "$0")")"

- 8,226
- 7
- 45
- 73
-
It is probably can be done without _realpath_: `dir=$(dirname $(which $0 2>/dev/null || echo $0))`. – anton_rh Jun 05 '23 at 09:19
So many answers, all plausible, each with pro's and con's & slightly differeing objectives (which should probably be stated for each). Here's another solution that meets a primary objective of both being clear and working across all systems, on all bash (no assumptions about bash versions, or readlink
or pwd
options), and reasonably does what you'd expect to happen (eg, resolving symlinks is an interesting problem, but isn't usually what you actually want), handle edge cases like spaces in paths, etc., ignores any errors and uses a sane default if there are any issues.
Each component is stored in a separate variable that you can use individually:
# script path, filename, directory
PROG_PATH=${BASH_SOURCE[0]} # this script's name
PROG_NAME=${PROG_PATH##*/} # basename of script (strip path)
PROG_DIR="$(cd "$(dirname "${PROG_PATH:-$PWD}")" 2>/dev/null 1>&2 && pwd)"

- 9,161
- 2
- 52
- 49
With tcsh, you can use the :h
variable modifier to retrieve the path.
One caveat is that if the script is executed as tcsh myscript.csh
, then you will only get the script name. A workaround is to validate the path as shown below.
#!/bin/tcsh
set SCRIPT_PATH = $0:h
if ( $SCRIPT_PATH == $0 ) then
set SCRIPT_PATH = "."
endif
$SCRIPT_PATH/compile.csh > $SCRIPT_PATH/results.txt
More information about variable modifiers can be found at https://learnxinyminutes.com/docs/tcsh/

- 387
- 3
- 4
one of the most robust ways i've found is
#!/bin/sh
relative_dir=`perl -e 'use Cwd "realpath";$pwd = realpath(shift); $pwd =~ s/\/[^\/]*$//; print $pwd' $0`
cd $relative_dir
works with symlinks and has worked for many of my coworkers no matter their choice of shell type

- 71
- 4
That should do the trick:
echo `pwd`/`dirname $0`
It might look ugly depending on how it was invoked and the cwd but should get you where you need to go (or you can tweak the string if you care how it looks).

- 155,785
- 88
- 678
- 743
-
1stackoverflow escape problem here: it surely should look like this: `\`pwd\`/\`dirname $0\`` but may still fail on symlinks – Andreas Covidiot Jul 24 '14 at 08:12