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
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)"
- and remove it without a command substitution - DIR="${DIR%x}"
. - anyone mkdir $'\n'
. - anyone #!/usr/bin/env bash
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && 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:
#!/usr/bin/env bash
SOURCE=${BASH_SOURCE[0]}
while [ -L "$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:
#!/usr/bin/env bash
SOURCE=${BASH_SOURCE[0]}
while [ -L "$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'
Answered 2023-09-20 19:59:26
source <script>
and bash <script>
: DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. - anyone 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 )"
- anyone dirname $(readlink -f $0)
is the right command. See gist.github.com/tvlooy/cbfbdb111a4ebad8b93e for a testcase - anyone dirname "$(readlink -f "$0")"
doesn't add complexity and is fair measure more robust for the minimal amount of trouble. - anyone dirname "$(readlink -f "$0")"
fails though in the case where the script is sourced, eg: /bin/bash -c . script.sh
... use $(dirname "$(readlink -f "${BASH_SOURCE[0]}")")
instead - anyone Use dirname "$0"
:
test.sh:
#!/usr/bin/env bash
echo "The script you are running has:"
echo "basename: [$(basename "$0")]"
echo "dirname : [$(dirname "$0")]"
echo "pwd : [$(pwd)]"
Using pwd
alone will not work if you are not running the script from the directory it is contained in.
[~]$ pwd
/home/matt
[~]$ ./test.sh
The script you are running has:
basename: [test.sh]
dirname : [/home/matt]
pwd : [/home/matt]
[~]$ cd /tmp
[~]$ ./test.sh
The script you are running has:
basename: [test.sh]
dirname : [/home/matt]
pwd : [/tmp]
Answered 2023-09-20 19:59:26
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. - anyone bash
and the hash-bang line explicitly mentions /bin/bash
I'd say it's pretty safe to depend on bashisms. - anyone 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?) - anyone 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. - anyone 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
:
#!/usr/bin/env 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
There is however one case where this doesn't work, when the script is sourced (instead of executed) in bash:
>>>$ cd /tmp
>>>$ . ~/whatdir.sh
pwd: /tmp
$0: bash
basename: bash
dirname: .
dirname/readlink: /tmp
Answered 2023-09-20 19:59:26
readlink
will not availabe in some platform in default installation. Try to avoid using it if you can - anyone export SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
- anyone -f
is not recognised as an option to readlink
. Using stat -f
instead does the job. Thanks - anyone 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]}`
- anyone $0
doesn't work if the file is sourced. You get -bash
instead of the script name. - anyone pushd . > '/dev/null';
SCRIPT_PATH="${BASH_SOURCE[0]:-$0}";
while [ -h "$SCRIPT_PATH" ];
do
cd "$( dirname -- "$SCRIPT_PATH"; )";
SCRIPT_PATH="$( readlink -f -- "$SCRIPT_PATH"; )";
done
cd "$( dirname -- "$SCRIPT_PATH"; )" > '/dev/null';
SCRIPT_PATH="$( pwd; )";
popd > '/dev/null';
It works for all versions, including
source
" aka .
(dot) operator.$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]:-$0}";
while [ -h "$SCRIPT_PATH" ];
do
cd "$( dirname -- "$SCRIPT_PATH"; )";
SCRIPT_PATH="$( readlink -f -- "$SCRIPT_PATH"; )";
done
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.
Answered 2023-09-20 19:59:26
readlink -f $(dirname "${VIRTUAL_ENV}")
; - anyone dirname "${SCRIPT_PATH}"
&& pwd)? But anyway great script! - anyone cd
out of its current directory in the hope of cd
ing 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) - anyone readlink -f
is GNU-specific. BSD readlink
does not have that option. - anyone Here is an easy-to-remember script:
DIR="$( dirname -- "${BASH_SOURCE[0]}"; )"; # Get the directory name
DIR="$( realpath -e -- "$DIR"; )"; # Resolve its full path if need be
Answered 2023-09-20 19:59:26
DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
- anyone realpath
from resolving "manually" with a loop of readlink
? Even the readlink
man page says Note realpath(1) is the preferred command to use for canonicalization functionality.
- anyone realpath
before dirname
, not after? If the script file itself is a symlink... It would give something like DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
. Actually very close to the answer proposed by Simon. - anyone realpath
(and man7.org/linux/man-pages/man1/realpath.1.html ) don't have the -f
option; is that supposed to be -e
@vaeVictis ? (That user added it.) - anyone You can use $BASH_SOURCE
:
#!/usr/bin/env bash
scriptdir="$( dirname -- "$BASH_SOURCE"; )";
Note that you need to use #!/bin/bash
and not #!/bin/sh
since it's a Bash extension.
Answered 2023-09-20 19:59:26
./foo/script
, then $(dirname $BASH_SOURCE)
is ./foo
. - anyone realpath
command to get full path of ./foo/script. So dirname $(realpath ./foo/script)
will give the path of script. - anyone Answered 2023-09-20 19:59:26
source
on these and cd $(dirname $0)
is easy to remember. - anyone ${BASH_SOURCE[0]}
instead of $0
will work with source my/script.sh
- anyone ${BASH_SOURCE[0]}
is not satisfactory at all. ${BASH_SOURCE:-0}
is much better. - anyone This should do it:
DIR="$(dirname "$(realpath "$0")")"
This works with symlinks and spaces in path.
Please see the man pages for dirname
and realpath
.
Please add a comment on how to support MacOS. I'm sorry I can verify it.
Answered 2023-09-20 19:59:26
./script.sh
shows .
instead of the full directory path - anyone stat
instead. But still, it shows .
if you are in 'this' dir. - anyone coreutils
from Homebrew and use greadlink
to get the -f
option on MacOS because it is *BSD under the covers and not Linux. - anyone realpath: command not found
on vanilla macOS, I suppose you have installed some homebrew package to get it working on your mac - anyone $BASH_SOURCE
works properly in my .bashrc
while $0
does not - anyone pwd
can be used to find the current working directory, and dirname
to find the directory of a particular file (command that was run, is $0
, so dirname $0
should give you the directory of the current script).
However, dirname
gives precisely the directory portion of the filename, which more likely than not is going to be relative to the current working directory. If your script needs to change directory for some reason, then the output from dirname
becomes meaningless.
I suggest the following:
#!/usr/bin/env bash
reldir="$( dirname -- "$0"; )";
cd "$reldir";
directory="$( pwd; )";
echo "Directory is ${directory}";
This way, you get an absolute, rather than a relative directory.
Since the script will be run in a separate Bash instance, there isn't any need to restore the working directory afterwards, but if you do want to change back in your script for some reason, you can easily assign the value of pwd
to a variable before you change directory, for future use.
Although just
cd "$( dirname -- "$0"; )";
solves the specific scenario in the question, I find having the absolute path to more more useful generally.
Answered 2023-09-20 19:59:26
dirname $0
&& pwd) - anyone SCRIPT_DIR=$( cd ${0%/*} && pwd -P )
Answered 2023-09-20 19:59:26
$0
nor pwd
are guaranteed to have the right information, depending on how the script is invoked. - anyone This gets the current working directory on Mac OS X v10.6.6 (Snow Leopard):
DIR=$(cd "$(dirname "$0")"; pwd)
Answered 2023-09-20 19:59:26
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.
Answered 2023-09-20 19:59:26
$(dirname "$(readlink -f "$BASH_SOURCE")")
Answered 2023-09-20 19:59:26
$BASH_SOURCE
over $0
, because it's explicit even for readers not well-versed in bash. $(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
- anyone $BASH_SOURCE
works while $0
does not in case of my .bashrc
(where both symlink AND sourcing is also used) - anyone This is Linux specific, but you could use:
SELF=$(readlink /proc/$$/fd/255)
Answered 2023-09-20 19:59:26
/proc/fd/$$/255
seems to point to the tty, not to a directory. For example, in my current login shell, file descriptors 0, 1, 2, and 255 all refer to /dev/pts/4
. In any case, the bash manual doesn't mention fd 255, so it's probably unwise to depend on this behavior.\ - anyone realpath ${BASH_SOURCE[0]};
would seem to be the best way to go. - anyone Here is a POSIX compliant one-liner:
SCRIPT_PATH=`dirname "$0"`; SCRIPT_PATH=`eval "cd \"$SCRIPT_PATH\" && pwd"`
# test
echo $SCRIPT_PATH
Answered 2023-09-20 19:59:26
cd
is configured to print the new path name. - anyone 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?".
Answered 2023-09-20 19:59:26
#!/bin/sh
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
PRG=`readlink "$PRG"`
done
scriptdir=`dirname "$PRG"`
Answered 2023-09-20 19:59:26
FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[-1]}")"
# OR, if you do NOT need it to work for **sourced** scripts too:
# FULL_PATH_TO_SCRIPT="$(realpath "$0")"
# OR, depending on which path you want, in case of nested `source` calls
# FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[0]}")"
# OR, add `-s` to NOT expand symlinks in the path:
# FULL_PATH_TO_SCRIPT="$(realpath -s "${BASH_SOURCE[-1]}")"
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"
...even when the called script is called from within another bash function or script, or when nested sourcing is being used!
For many cases, all you need to acquire is the full path to the script you just called. This can be easily accomplished using realpath
. Note that realpath
is part of GNU coreutils. If you don't have it already installed (it comes default on Ubuntu), you can install it with sudo apt update && sudo apt install coreutils
.
get_script_path.sh (for the latest version of this script, see get_script_path.sh in my eRCaGuy_hello_world repo):
#!/bin/bash
# A. Obtain the full path, and expand (walk down) symbolic links
# A.1. `"$0"` works only if the file is **run**, but NOT if it is **sourced**.
# FULL_PATH_TO_SCRIPT="$(realpath "$0")"
# A.2. `"${BASH_SOURCE[-1]}"` works whether the file is sourced OR run, and even
# if the script is called from within another bash function!
# NB: if `"${BASH_SOURCE[-1]}"` doesn't give you quite what you want, use
# `"${BASH_SOURCE[0]}"` instead in order to get the first element from the array.
FULL_PATH_TO_SCRIPT="$(realpath "${BASH_SOURCE[-1]}")"
# B.1. `"$0"` works only if the file is **run**, but NOT if it is **sourced**.
# FULL_PATH_TO_SCRIPT_KEEP_SYMLINKS="$(realpath -s "$0")"
# B.2. `"${BASH_SOURCE[-1]}"` works whether the file is sourced OR run, and even
# if the script is called from within another bash function!
# NB: if `"${BASH_SOURCE[-1]}"` doesn't give you quite what you want, use
# `"${BASH_SOURCE[0]}"` instead in order to get the first element from the array.
FULL_PATH_TO_SCRIPT_KEEP_SYMLINKS="$(realpath -s "${BASH_SOURCE[-1]}")"
# You can then also get the full path to the directory, and the base
# filename, like this:
SCRIPT_DIRECTORY="$(dirname "$FULL_PATH_TO_SCRIPT")"
SCRIPT_FILENAME="$(basename "$FULL_PATH_TO_SCRIPT")"
# Now print it all out
echo "FULL_PATH_TO_SCRIPT = \"$FULL_PATH_TO_SCRIPT\""
echo "SCRIPT_DIRECTORY = \"$SCRIPT_DIRECTORY\""
echo "SCRIPT_FILENAME = \"$SCRIPT_FILENAME\""
IMPORTANT note on nested source
calls: if "${BASH_SOURCE[-1]}"
above doesn't give you quite what you want, try using "${BASH_SOURCE[0]}"
instead. The first (0
) index gives you the first entry in the array, and the last (-1
) index gives you the last last entry in the array. Depending on what it is you're after, you may actually want the first entry. I discovered this to be the case when I sourced ~/.bashrc
with . ~/.bashrc
, which sourced ~/.bash_aliases
with . ~/.bash_aliases
, and I wanted the realpath
(with expanded symlinks) to the ~/.bash_aliases
file, NOT to the ~/.bashrc
file. Since these are nested source
calls, using "${BASH_SOURCE[0]}"
gave me what I wanted: the expanded path to ~/.bash_aliases
! Using "${BASH_SOURCE[-1]}"
, however, gave me what I did not want: the expanded path to ~/.bashrc
.
Example command and output:
~/GS/dev/eRCaGuy_hello_world/bash$ ./get_script_path.sh
FULL_PATH_TO_SCRIPT = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash/get_script_path.sh"
SCRIPT_DIRECTORY = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash"
SCRIPT_FILENAME = "get_script_path.sh"
. get_script_path.sh
or source get_script_path.sh
(the result is the exact same as above because I used "${BASH_SOURCE[-1]}"
in the script instead of "$0"
):
~/GS/dev/eRCaGuy_hello_world/bash$ . get_script_path.sh
FULL_PATH_TO_SCRIPT = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash/get_script_path.sh"
SCRIPT_DIRECTORY = "/home/gabriel/GS/dev/eRCaGuy_hello_world/bash"
SCRIPT_FILENAME = "get_script_path.sh"
If you use "$0"
in the script instead of "${BASH_SOURCE[-1]}"
, you'll get the same output as above when running the script, but this undesired output instead when sourcing the script:
~/GS/dev/eRCaGuy_hello_world/bash$ . get_script_path.sh
FULL_PATH_TO_SCRIPT = "/bin/bash"
SCRIPT_DIRECTORY = "/bin"
SCRIPT_FILENAME = "bash"
And, apparently if you use "$BASH_SOURCE"
instead of "${BASH_SOURCE[-1]}"
, it will not work if the script is called from within another bash function. So, using "${BASH_SOURCE[-1]}"
is therefore the best way to do it, as it solves both of these problems! See the references below.
Difference between realpath
and realpath -s
:
Note that realpath
also successfully walks down symbolic links to determine and point to their targets rather than pointing to the symbolic link. If you do NOT want this behavior (sometimes I don't), then add -s
to the realpath
command above, making that line look like this instead:
# Obtain the full path, but do NOT expand (walk down) symbolic links; in
# other words: **keep** the symlinks as part of the path!
FULL_PATH_TO_SCRIPT="$(realpath -s "${BASH_SOURCE[-1]}")"
This way, symbolic links are NOT expanded. Rather, they are left as-is, as symbolic links in the full path.
The code above is now part of my eRCaGuy_hello_world repo in this file here: bash/get_script_path.sh. Reference and run this file for full examples both with and withOUT symlinks in the paths. See the bottom of the file for example output in both cases.
BASH_SOURCE
variable: Unix & Linux: determining path to sourced shell scriptBASH_SOURCE
is actually an array, and we want the last element from it for it to work as expected inside a function (hence why I used "${BASH_SOURCE[-1]}"
in my code here): Unix & Linux: determining path to sourced shell scriptman bash
--> search for BASH_SOURCE
:
BASH_SOURCE
An array variable whose members are the source filenames where the corresponding shell function names in the
FUNCNAME
array variable are defined. The shell function${FUNCNAME[$i]}
is defined in the file${BASH_SOURCE[$i]}
and called from${BASH_SOURCE[$i+1]}
.
Answered 2023-09-20 19:59:26
${BASH_SOURCE[-1]}
and ${BASH_SOURCE[0]}
? I know -1
retrieves the last element from the array and 0
retrieves the first one but in which case do I want to use one over the other ? - anyone IMPORTANT note on nested source calls
section in the answer. It has to do with nested sourcing, when one script which you source sources another script. - anyone 1
would have given me the same result as -1
since I believe the array only had 2 elements in it, so that would have been the last element in both cases. - anyone Here is the simple, correct way:
actual_path=$(readlink -f "${BASH_SOURCE[0]}")
script_dir=$(dirname "$actual_path")
Explanation:
${BASH_SOURCE[0]}
- the full path to the script. The value of this will be correct even when the script is being sourced, e.g. source <(echo 'echo $0')
prints bash, while replacing it with ${BASH_SOURCE[0]}
will print the full path of the script. (Of course, this assumes you're OK taking a dependency on Bash.)
readlink -f
- Recursively resolves any symlinks in the specified path. This is a GNU extension, and not available on (for example) BSD systems. If you're running a Mac, you can use Homebrew to install GNU coreutils
and supplant this with greadlink -f
.
And of course dirname
gets the parent directory of the path.
Answered 2023-09-20 19:59:26
greadlink -f
unfortunately doesn't work effectively when source
ing the script on Mac :( - anyone I tried all of these and none worked. One was very close, but it 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"`"
Answered 2023-09-20 19:59:26
ln -s ../bin64/foo /usr/bin/foo
). - anyone Try using:
real=$(realpath "$(dirname "$0")")
Answered 2023-09-20 19:59:26
$0
for ${BASH_SOURCE[0]}
so that this method will work anywhere, including in a function. - anyone dirname
because the last part of $0
may be a symlink that points to a file that is not in the same directory as the symlink itself. The solution described in this answer just gets the path of the directory where the symlink it stored, not the directory of the target. Furthermore, this solution is missing quoting. It will not work if the path contains special characters. - anyone dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
- anyone 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.
Answered 2023-09-20 19:59:26
SCRIPT_DIR=''; pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && { SCRIPT_DIR=$PWD; popd > /dev/null; }
- anyone 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. - anyone pushd
popd
dangers could be avoided simply by dropping them and using cd
+ pwd
enclosed in a command substitution instead. SCRIPT_DIR=$(...)
- anyone I would use something like this:
# Retrieve the full pathname of the called script
scriptPath=$(which $0)
# Check whether the path is a link or not
if [ -L $scriptPath ]; then
# It is a link then retrieve the target path and get the directory name
sourceDir=$(dirname $(readlink -f $scriptPath))
else
# Otherwise just get the directory name of the script path
sourceDir=$(dirname $scriptPath)
fi
Answered 2023-09-20 19:59:26
sh
too! Problem with simple dirname "$0"
based solutions: If the script is in the $PATH
and is invoked without path, they will give wrong result. - anyone PATH
, $0
will contain the absolute filename. If the script is invoked with a relative or absolute filename containing a /
, $0
will contain that. - anyone For systems having GNU coreutils readlink
(for example, Linux):
$(readlink -f "$(dirname "$0")")
There's no need to use BASH_SOURCE
when $0
contains the script filename.
Answered 2023-09-20 19:59:26
dirname
call. Needed if the directory path contains spaces. - anyone $_
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.
Answered 2023-09-20 19:59:26
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. - anyone These are short ways to get script information:
Folders and files:
Script: "/tmp/src dir/test.sh"
Calling folder: "/tmp/src dir/other"
Using these commands:
echo Script-Dir : `dirname "$(realpath $0)"`
echo Script-Dir : $( cd ${0%/*} && pwd -P )
echo Script-Dir : $(dirname "$(readlink -f "$0")")
echo
echo Script-Name : `basename "$(realpath $0)"`
echo Script-Name : `basename $0`
echo
echo Script-Dir-Relative : `dirname "$BASH_SOURCE"`
echo Script-Dir-Relative : `dirname $0`
echo
echo Calling-Dir : `pwd`
And I got this output:
Script-Dir : /tmp/src dir
Script-Dir : /tmp/src dir
Script-Dir : /tmp/src dir
Script-Name : test.sh
Script-Name : test.sh
Script-Dir-Relative : ..
Script-Dir-Relative : ..
Calling-Dir : /tmp/src dir/other
Also see: https://pastebin.com/J8KjxrPF
Answered 2023-09-20 19:59:26
This works in Bash 3.2:
path="$( dirname "$( which "$0" )" )"
If you have a ~/bin
directory in your $PATH
, you have A
inside this directory. It sources the script ~/bin/lib/B
. You know where the included script is relative to the original one, in the lib
subdirectory, but not where it is relative to the user's current directory.
This is solved by the following (inside A
):
source "$( dirname "$( which "$0" )" )/lib/B"
It doesn't matter where the user is or how he/she calls the script. This will always work.
Answered 2023-09-20 19:59:26
which
is very debatable. type
, hash
, and other builtins do the same thing better in bash. which
is kindof more portable, though it really isn't the same which
used in other shells like tcsh, that has it as a builtin. - anyone which
being an external tool, you have no reason to believe it behaves identically to the parent shell. - anyone I've compared many of the answers given, and came up with some more compact solutions. These seem to handle all of the crazy edge cases that arise from your favorite combination of:
script
, bash script
, bash -c script
, source script
, or . script
If you're running from Linux, it seems that using the proc
handle is the best solution to locate the fully resolved source of the currently running script (in an interactive session, the link points to the respective /dev/pts/X
):
resolved="$(readlink /proc/$$/fd/255 && echo X)" && resolved="${resolved%$'\nX'}"
This has a small bit of ugliness to it, but the fix is compact and easy to understand. We aren't using bash primitives only, but I'm okay with that because readlink
simplifies the task considerably. The echo X
adds an X
to the end of the variable string so that any trailing whitespace in the filename doesn't get eaten, and the parameter substitution ${VAR%X}
at the end of the line gets rid of the X
. Because readlink
adds a newline of its own (which would normally be eaten in the command substitution if not for our previous trickery), we have to get rid of that, too. This is most easily accomplished using the $''
quoting scheme, which lets us use escape sequences such as \n
to represent newlines (this is also how you can easily make deviously named directories and files).
The above should cover your needs for locating the currently running script on Linux, but if you don't have the proc
filesystem at your disposal, or if you're trying to locate the fully resolved path of some other file, then maybe you'll find the below code helpful. It's only a slight modification from the above one-liner. If you're playing around with strange directory/filenames, checking the output with both ls
and readlink
is informative, as ls
will output "simplified" paths, substituting ?
for things like newlines.
absolute_path=$(readlink -e -- "${BASH_SOURCE[0]}" && echo x) && absolute_path=${absolute_path%?x}
dir=$(dirname -- "$absolute_path" && echo x) && dir=${dir%?x}
file=$(basename -- "$absolute_path" && echo x) && file=${file%?x}
ls -l -- "$dir/$file"
printf '$absolute_path: "%s"\n' "$absolute_path"
Answered 2023-09-20 19:59:26
/dev/pts/30
with bash on Ubuntu 14.10 Desktop. - anyone echo $resolved
, I saved it as d
, chmod +x d
, ./d
. - anyone #!/bin/bash
- anyone Incredible how simple can be, no matter how you call the script:
#!/bin/bash
#
the_source=$(readlink -f ${BASH_SOURCE[0]})
the_dirname=$(dirname ${the_source})
echo "the_source: ${the_source}"
echo "the_dirname: ${the_dirname}"
Run from anywhere:
user@computer:~/Downloads/temp$ ./test.sh
Output:
the_source: /home/user/Downloads/temp/test.sh
the_dirname: /home/user/Downloads/temp
Answered 2023-09-20 19:59:26
.bin
directory created by npm/yarn
and you need to now original script location to relatively reference static resources bundled in your npm package... - anyone 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: "$@"\n" ;} # Borrow a horrible signal name.
# Probably best not to install as 'pathfull', if you can avoid it.
pathfull(){ cd "$(dirname "$@")"; link="$(readlink "$(basename "$@")")"
## 'test and 'ls' report different status for bad symlinks, so we use this.
if [ ! -e "$@" ]; then if $(ls -d "$@" 2>/dev/null) 2>/dev/null; then
errnoent 1>&2; exit 1; elif [ ! -e "$@" -a "$link" = "$@" ]; then
recurses 1>&2; exit 1; elif [ ! -e "$@" ] && [ ! -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 "$@" | cut -c1)" = '/' ]; then
printf "$@\n"; exit 0; else printf "$(pwd)/$(basename "$@")\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 "$@"
fi
Answered 2023-09-20 19:59:26