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")"
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
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)"
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.
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).
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.
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.
Let's make it a POSIX oneliner:
a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(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.jasan.tk/posts/2017-05-11-posix_shell_dirname_replacement/
Inspired by blueyed’s answer
read < <(readlink -f $0 | xargs dirname)
cd $REPLY
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
.
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'"
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.
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).
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")"
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
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)"
Inspired by blueyed’s answer
read < <(readlink -f $0 | xargs dirname)
cd $REPLY
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
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
.
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.
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
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).
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.
BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"
cd $(dirname $(readlink -f $0))
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'"
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.
Let's make it a POSIX oneliner:
a="/$0"; a=${a%/*}; a=${a#/}; a=${a:-.}; BASEDIR=$(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.jasan.tk/posts/2017-05-11-posix_shell_dirname_replacement/
BASE_DIR="$(cd "$(dirname "$0")"; pwd)";
echo "BASE_DIR => $BASE_DIR"
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.
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}"
Source: Stackoverflow.com