In a script I get in $0 the possible relative path to it. For converting it to absolute I've found this solution which I don't understand:

abspath=$(cd ${0%/*} && echo $PWD/${0##*/})

My problem is the magic inside of ${0%/*} and ${0##*/}. It looks like the former extracts the dirname and the latter extracts the filename, I just don't get how.

  • 2
    That uses parameter expansion, but it didn't work for me. If your script is only going to be used on Linux, you can use readlink -f $0 to get the canonical path. – Shawn J. Goff Mar 18 '11 at 13:22
  • 1
    @Shawn: 1 great comment vote because you introduced the right thinking: "That uses parameter expansion, but it didn't work for me". The dirname util is useful here. – D4RIO Mar 18 '11 at 14:09
  • mywiki.wooledge.org/BashFAQ/028 and cyberciti.biz/faq/… says BASH_SOURCE is better than $0, as $0 gives the user's typed in command, which might not be the currently executing script. – Joel Purra Jun 26 '14 at 11:06


${string%substring} deletes shortest match of $substring from the end of $string.

${string##substring} deletes longest match of $substring from the start of $string.

Your example:

abspath=$(cd ${0%/*} && echo $PWD/${0##*/})

${0%/*} deletes everything after the last slash, giving you the directory name of the script (which might be a relative path).

${0##*/} deletes everything upto the last slash, giving you just the name of the script.

So, this command changes to the directory of the script and concatenates the current working directory (given by $PWD) and the name of the script giving you the absolute path.

To see what is going on try:

echo ${0%/*}
echo ${0##*/}
|improve this answer|||||
  • Add double quotes around all variable expansions to cope with (almost all) file names containing shell special characters. – Gilles 'SO- stop being evil' Mar 18 '11 at 20:42

Shawn had the simplest solution: readlink -f $0. If you want to be absolutely sure to handle weird file names, you can use this:

absolute_path_x="$(readlink -fn -- "$0"; echo x)"


|improve this answer|||||
  • 1
    Good to see final newlines correctly handled. Unfortunately readlink -fn is specific to Linux, NetBSD and OpenBSD. – Gilles 'SO- stop being evil' Mar 18 '11 at 20:41

Here is a safer and more readable way to do this job:

abspath=$(unset CDPATH && cd "$(dirname "$0")" && echo $PWD/$(basename "$0"))


  • If $0 is a bare filename with no preceding path, the original script will fail but the one given here will work. (Not a problem with $0 but could be in other applications.)
  • Either approach will fail if the path to the file doesn't actually exist. (Not a problem with $0, but could be in other applications.)
  • The unset is essential if your user may have CDPATH set.
  • Unlike readlink -f or realpath, this will work on non-Linux versions of Unix (e.g., Mac OS X).
|improve this answer|||||

If you want to learn Shell Parameter Expansion, you can read it from here, but Expansion isn't always a good choice. In this case, almost every Unix like system has 2 good utils:


The first will extract the filename, while the second will extract the path, so, if you have $0, say:

dirname $0

And you'ill get the path.


|improve this answer|||||
  • dirname can return relative paths – opticyclic Apr 18 '13 at 21:02

Introducing pwd, the bash builtin. Also found in the GNU coreutils package.

$ sh ./cygdrive/c/cygwin/home/../../../../home/jaroslav/tmp/somewhere.sh
$0: ./cygdrive/c/cygwin/home/../../../../home/jaroslav/tmp/somewhere.sh
cheeky binary from /home/jaroslav/tmp (/home/jaroslav/tmp)

$ cat /home/jaroslav/tmp/somewhere.sh

abs=$( cd `dirname "$0"` ; pwd -P)
absBin=$( cd `dirname "$0"` ; /bin/pwd -P)
echo \$0: $0
echo cheeky binary from $abs \($absBin\)
|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.