How do I get the directory where a Bash script is located from within the script itself?
#!/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'
How can I find script's directory?
You need to call os.path.realpath
on __file__
, so that when __file__
is a filename without the path you still get the dir path:
import os
print(os.path.dirname(os.path.realpath(__file__)))
Unix shell script find out which directory the script file resides?
In Bash, you should get what you need like this:
#!/usr/bin/env bash
BASEDIR=$(dirname "$0")
echo "$BASEDIR"
get script directory name - Python
import os
os.path.basename(os.path.dirname(os.path.realpath(__file__)))
Broken down:
currentFile = __file__ # May be 'my_script', or './my_script' or
# '/home/user/test/my_script.py' depending on exactly how
# the script was run/loaded.
realPath = os.path.realpath(currentFile) # /home/user/test/my_script.py
dirPath = os.path.dirname(realPath) # /home/user/test
dirName = os.path.basename(dirPath) # test
What's the best way to determine the location of the current PowerShell script?
PowerShell 3+
# This is an automatic variable set to the current file's/module's directory
$PSScriptRoot
PowerShell 2
Prior to PowerShell 3, there was not a better way than querying theMyInvocation.MyCommand.Definition
property for general scripts. I had the following line at the top of essentially every PowerShell script I had:
$scriptPath = split-path -parent $MyInvocation.MyCommand.Definition
How do I find directory of the Python running script from inside the script?
To get the directory that contains the module you are running:
import os
path = os.path.dirname(os.path.realpath(__file__))
Or if you want the directory from which the script was invoked:
import os
path = os.getcwd()
From the docs:
__file__
is the pathname of the file from which the module was loaded, if it was loaded from a file.
Depending on how the script is called, this may be a relative path from os.getcwd()
, so os.path.realpath(__file__)
will convert this to an absolute path (or do nothing is the __file__
is already an absolute path). os.path.dirname()
will then return the full directory by stripping off the filename.
Shell script current directory?
The current(initial) directory of shell script is the directory from which you have called the script.
How to get script directory in POSIX sh?
The POSIX-shell (sh
) counterpart of $BASH_SOURCE
is $0
. see bottom for background info
Caveat: The crucial difference is that if your script is being sourced (loaded into the current shell with .
), the snippets below will not work properly. explanation further below
Note that I've changed DIR
to dir
in the snippets below, because it's better not to use all-uppercase variable names so as to avoid clashes with environment variables and special shell variables.
The CDPATH=
prefix takes the place of > /dev/null
in the original command: $CDPATH
is set to a null string so as to ensure that cd
never echoes anything.
In the simplest case, this will do (the equivalent of the OP's command):
dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
If you also want to resolve the resulting directory path to its ultimate target in case the directory and/or its components are symlinks, add -P
to the pwd
command:
dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
Caveat: This is NOT the same as finding the script's own true directory of origin:
Let's say your script foo
is symlinked to /usr/local/bin/foo
in the $PATH
, but its true path is /foodir/bin/foo
.
The above will still report /usr/local/bin
, because the symlink resolution (-P
) is applied to the directory, /usr/local/bin
, rather than to the script itself.
To find the script's own true directory of origin, you'd have to inspect the script's path to see if it's a symlink and, if so, follow the (chain of) symlinks to the ultimate target file, and then extract the directory path from the target file's canonical path.
GNU's readlink -f
(better: readlink -e
) could do that for you, but readlink
is not a POSIX utility.
While BSD platforms, including macOS, have a readlink
utility too, on macOS it doesn't support -f
's functionality. That said, to show how simple the task becomes if readlink -f
is available: dir=$(dirname "$(readlink -f -- "$0")")
.
In fact, there is no POSIX utility for resolving file symlinks.
There are ways to work around that, but they're cumbersome and not fully robust:
The following, POSIX-compliant shell function implements what GNU's readlink -e
does and is a reasonably robust solution that only fails in two rare edge cases:
- paths with embedded newlines (very rare)
- filenames containing literal string
->
(also rare)
With this function, named rreadlink
, defined, the following determines the script's true directory path of origin:
dir=$(dirname -- "$(rreadlink "$0")")
Note: If you're willing to assume the presence of a (non-POSIX) readlink
utility - which would cover macOS, FreeBSD and Linux - a similar, but simpler solution can be found in this answer to a related question.
rreadlink()
source code - place before calls to it in scripts:
rreadlink() ( # Execute the function in a *subshell* to localize variables and the effect of `cd`.
target=$1 fname= targetDir= CDPATH=
# Try to make the execution environment as predictable as possible:
# All commands below are invoked via `command`, so we must make sure that `command`
# itself is not redefined as an alias or shell function.
# (Note that command is too inconsistent across shells, so we don't use it.)
# `command` is a *builtin* in bash, dash, ksh, zsh, and some platforms do not even have
# an external utility version of it (e.g, Ubuntu).
# `command` bypasses aliases and shell functions and also finds builtins
# in bash, dash, and ksh. In zsh, option POSIX_BUILTINS must be turned on for that
# to happen.
{ \unalias command; \unset -f command; } >/dev/null 2>&1
[ -n "$ZSH_VERSION" ] && options[POSIX_BUILTINS]=on # make zsh find *builtins* with `command` too.
while :; do # Resolve potential symlinks until the ultimate target is found.
[ -L "$target" ] || [ -e "$target" ] || { command printf '%s\n' "ERROR: '$target' does not exist." >&2; return 1; }
command cd "$(command dirname -- "$target")" # Change to target dir; necessary for correct resolution of target path.
fname=$(command basename -- "$target") # Extract filename.
[ "$fname" = '/' ] && fname='' # !! curiously, `basename /` returns '/'
if [ -L "$fname" ]; then
# Extract [next] target path, which may be defined
# *relative* to the symlink's own directory.
# Note: We parse `ls -l` output to find the symlink target
# which is the only POSIX-compliant, albeit somewhat fragile, way.
target=$(command ls -l "$fname")
target=${target#* -> }
continue # Resolve [next] symlink target.
fi
break # Ultimate target reached.
done
targetDir=$(command pwd -P) # Get canonical dir. path
# Output the ultimate target's canonical path.
# Note that we manually resolve paths ending in /. and /.. to make sure we have a normalized path.
if [ "$fname" = '.' ]; then
command printf '%s\n' "${targetDir%/}"
elif [ "$fname" = '..' ]; then
# Caveat: something like /var/.. will resolve to /private (assuming /var@ -> /private/var), i.e. the '..' is applied
# AFTER canonicalization.
command printf '%s\n' "$(command dirname -- "${targetDir}")"
else
command printf '%s\n' "${targetDir%/}/$fname"
fi
)
To be robust and predictable, the function uses command
to ensure that only shell builtins or external utilities are called (ignores overloads in the forms of aliases and functions).
It's been tested in recent versions of the following shells: bash
, dash
, ksh
, zsh
.
How to handle sourced invocations:
tl;dr:
Using POSIX features only:
You cannot determine the script's path in a sourced invocation (except in
zsh
, which, however, doesn't usually act assh
).You can detect whether or not your script is being sourced ONLY if your script is being sourced directly by the shell (such as in a shell profile/initialization file; possibly via a chain of sourcings), by comparing
$0
to the shell executable name/path (except inzsh
, where, as noted$0
is truly the current script's path). By contrast (except inzsh
), a script being sourced from another script that itself was directly invoked, contains that script's path in$0
.To solve these problems,
bash
,ksh
, andzsh
have nonstandard features that do allow determining the actual script path even in sourced scenarios and also detecting whether a script is being sourced or not; for instance, inbash
,$BASH_SOURCE
always contains the running script's path, whether it's being sourced or not, and[[ $0 != "$BASH_SOURCE" ]]
can be used to test whether the script is being sourced.- This answer indirectly shows these techniques, in the context of determining whether a given script is being sourced.
To show why this cannot be done, let's analyze the command from Walter A's answer:
# NOT recommended - see discussion below.
DIR=$( cd -P -- "$(dirname -- "$(command -v -- "$0")")" && pwd -P )
- (Two asides:
- Using
-P
twice is redundant - it's sufficient to use it withpwd
. - The command is missing silencing of
cd
's potential stdout output, if$CDPATH
happens to be set.)
- Using
command -v -- "$0"
command -v -- "$0"
is designed to cover one additional scenario: if the script is being sourced from an interactive shell,$0
typically contains the mere filename of the shell executable (sh
), in which casedirname
would simply return.
(because that's whatdirname
invariably does when given a argument without a path component).command -v -- "$0"
then returns that shell's absolute path through a$PATH
lookup (/bin/sh
). Note, however, that login shells on some platforms (e.g., OSX) have their filename prefixed with-
in$0
(-sh
), in which casecommand -v -- "$0"
doesn't work as intended (returns an empty string).- Conversely,
command -v -- "$0"
can misbehave in two non-sourced scenarios in which the shell executable,sh
, is directly invoked, with the script as an argument:- if the script itself is not executable:
command -v -- "$0"
may return an empty string, depending on what specific shell acts assh
on a given system:bash
,ksh
, andzsh
return an empty string; onlydash
echoes$0
The POSIX spec. forcommand
doesn't explicitly say whethercommand -v
, when applied to a filesystem path, should only return executable files - which is whatbash
,ksh
, andzsh
do - but you can argue that it is implied by the very purpose ofcommand
; curiously,dash
, which is usually the most compliant POSIX citizen, is deviating from the standard here. By contrast,ksh
is the lone model citizen here, because it is the only one that reports executable files only and reports them with an absolute (albeit not normalized) path, as the spec requires. - if the script is executable, but not in the
$PATH
, and the invocation uses its mere filename (e.g.,sh myScript
),command -v -- "$0"
will also return the empty string, except indash
.
- if the script itself is not executable:
- Given that the script's directory cannot be determined when the script is being sourced - because
$0
then doesn't contain that information (except inzsh
, which doesn't usually act assh
) - there's no good solution to this problem.- Returning the shell executable's directory path in that situation is of limited use - it is, after all, not the script's directory - except perhaps to later use that path in a test to determine whether or not the script is being sourced.
- A more reliable approach would be to simply test
$0
directly:[ "$0" = "sh" ] || [ "$0" = "-sh" ] || [ "$0" = "/bin/sh" ]
- A more reliable approach would be to simply test
- However, even that doesn't work if the script is being sourced from another script (that was itself directly invoked), because
$0
then simply contains the sourcing script's path.
- Returning the shell executable's directory path in that situation is of limited use - it is, after all, not the script's directory - except perhaps to later use that path in a test to determine whether or not the script is being sourced.
- Given the limited usefulness of
command -v -- "$0"
in sourced scenarios and the fact that it breaks two non-sourced scenarios, my vote is for NOT using it, which leaves us with:- All non-sourced scenarios are covered.
- In sourced invocations, you cannot determine the script's path, and at best, in limited circumstances, you can detect whether or not sourcing is occurring:
- When sourced directly by the shell (such as from a shell profile/initialization file),
$dir
ends up either containing.
, if the shell executable was invoked as a mere filename (applyingdirname
to a mere filename always returns.
), or the shell executable's directory path otherwise..
cannot be reliably distinguished from a non-sourced invocation from the current directory. - When sourced from another script (that was itself not also sourced),
$0
contains that script's path, and the script being sourced has no way of telling whether that's the case.
- When sourced directly by the shell (such as from a shell profile/initialization file),
Background information:
POSIX defines the behavior of $0
with respect to shell scripts here.
Essentially, $0
should reflect the path of the script file as specified, which implies:
Do NOT rely on
$0
containing an absolute path.$0
contains an absolute path only if:you explicitly specify an absolute path; e.g.:
~/bin/myScript
(assuming the script itself is executable)sh ~/bin/myScript
you invoke an executable script by mere filename, which requires that it both be executable and in the
$PATH
; behind the scenes, the system transformsmyScript
into an absolute path and then executes it; e.g.:myScript # executes /home/jdoe/bin/myScript, for instance
In all other cases,
$0
will reflect the script path as specified:- When explicitly invoking
sh
with a script, this can be a mere filename (e.g.,sh myScript
) or a relative path (e.g.,sh ./myScript
) - When invoking an executable script directly, this can be a relative path (e.g.,
./myScript
- note that a mere filename would only find scripts in the$PATH
).
- When explicitly invoking
In practice, bash
, dash
, ksh
, and zsh
all exhibit this behavior.
By contrast, POSIX does NOT mandate the value of $0
when sourcing a script (using the special built-in utility .
("dot")), so you cannot rely on it, and, in practice, behavior differs across shells.
- Thus, you cannot blindly use
$0
when your script is being sourced and expect standardized behavior.- In practice,
bash
,dash
, andksh
leave$0
untouched when sourcing scripts, meaning that$0
contains the caller's$0
value, or, more accurately, the$0
value of the most recent caller in the call chain that hasn't been sourced itself; thus,$0
may point either to the shell's executable or to the path of another (directly invoked) script that sourced the current one. - By contrast,
zsh
, as the lone dissenter, actually does report the current script's path in$0
. Conversely,$0
will provide no indication as to whether the script is being sourced or not. - In short: using POSIX features only, you can neither tell reliably whether the script at hand is being sourced, nor what the script at hand's path is, nor what the relationship of
$0
to the current script's path is.
- In practice,
- If you do need to handle this situation, you must identify the specific shell at hand and access its specific non-standard features:
bash
,ksh
, andzsh
all offer their own ways of obtaining the running script's path, even when it's being sourced.
For the sake of completeness: the value of $0
in other contexts:
- Inside a shell function, POSIX mandates that
$0
remain unchanged; therefore, whatever value it has outside the function, it'll have inside as well.- In practice,
bash
,dash
, andksh
do behave that way. - Again,
zsh
is the lone dissenter and reports the function's name.
- In practice,
- In a shell that accepted a command string via the
-c
option on startup, it's the first operand (non-option argument) that sets$0
; e.g.:sh -c 'echo \$0: $0 \$1: $1' foo one # -> '$0: foo $1: one'
bash
,dash
,ksh
, andzsh
all behave that way.
- Otherwise, in a shell not executing a script file,
$0
is the value of the first argument that the shell's parent process passed - typically, that's the shell's name or path (e.g.sh
, or/bin/sh
); this includes:- an interactive shell
- Caveat: some platforms, notably OSX, always create login shells when creating interactive shells, and prepend
-
to the shell name before placing it in$0
, so as to signal to the shell that it is a _login shell; thus, by default,$0
reports-bash
, notbash
, in interactive shells on OSX.
- Caveat: some platforms, notably OSX, always create login shells when creating interactive shells, and prepend
- a shell that reads commands from stdin
- this also applies to piping a script file to the shell via stdin (e.g.,
sh < myScript
)
- this also applies to piping a script file to the shell via stdin (e.g.,
bash
,dash
,ksh
, andzsh
all behave that way.
- an interactive shell
Reliable way for a Bash script to get the full path to itself
Here's what I've come up with (edit: plus some tweaks provided by sfstewman, levigroker, Kyle Strand, and Rob Kennedy), that seems to mostly fit my "better" criteria:
SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
That SCRIPTPATH
line seems particularly roundabout, but we need it rather than SCRIPTPATH=`pwd`
in order to properly handle spaces and symlinks.
The inclusion of output redirection (>/dev/null 2>&1
) handles the rare(?) case where cd
might produce output that would interfere with the surrounding $( ... )
capture. (Such as cd
being overridden to also ls
a directory after switching to it.)
Note also that esoteric situations, such as executing a script that isn't coming from a file in an accessible file system at all (which is perfectly possible), is not catered to there (or in any of the other answers I've seen).
The --
after cd
and before "$0"
are in case the directory starts with a -
.
Related Topics
How to Percent-Encode Url Parameters in Python
Disable Tensorflow Debugging Information
Extracting Text from a PDF File Using PDFminer in Python
Plotting a 3D Cube, a Sphere and a Vector in Matplotlib
Automatically Create Requirements.Txt
Compare Two Dataframes and Output Their Differences Side-By-Side
How to Install Python Packages on Windows
How to Use Subprocess Popen Python
Way to Change Google Chrome User Agent in Selenium
Transpose Column to Row with Spark
Python Regex Engine - "Look-Behind Requires Fixed-Width Pattern" Error