views:

5622

answers:

9

Given an absolute or relative path (in a Unix-like system), I would like to determine the full path of the target after resolving any intermediate symlinks. Bonus points for also resolving ~username notation at the same time.

If the target is a directory, it might be possible to chdir() into the directory and then call getcwd(), but I really want to do this from a shell script rather than writing a C helper. Unfortunately, shells have a tendency to try to hide the existence of symlinks from the user (this is bash on OS X):

$ ls -ld foo bar
drwxr-xr-x   2 greg  greg  68 Aug 11 22:36 bar
lrwxr-xr-x   1 greg  greg   3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd
/Users/greg/tmp/foo
$

What I want is a function resolve() such that when executed from the tmp directory in the above example, resolve("foo") == "/Users/greg/tmp/bar".

+10  A: 

According to the standards, pwd -P should return the path with symlinks resolved.

C function char *getcwd(char *buf, size_t size) from unistd.h should have the same behaviour.

getcwd pwd

kauppi
This just works for (the current) directory? If the target is a file, it will not give anything...
dala
+3  A: 

Ah, -P does it, thanks! The man page lies:

-P Print the physical path to the current working directory, with symbolic links in the path resolved. This is the default.

Update: The man page doesn't lie. My shell was still lying to me, since it was running its builtin pwd which apparently has different defaults. /bin/pwd behaves as the man page states.

Greg Hewgill
A: 

Common shell scripts often have to find their "home" directory even if they are invoked as a symlink. The script thus have to find their "real" position from just $0.

cat `mvn`

on my system prints a script containing the following, which should be a good hint at what you need.

if [ -z "$M2_HOME" ] ; then
  ## resolve links - $0 may be a link to maven's home
  PRG="$0"

  # need this for relative symlinks
  while [ -h "$PRG" ] ; do
    ls=`ls -ld "$PRG"`
    link=`expr "$ls" : '.*-> \(.*\)$'`
    if expr "$link" : '/.*' > /dev/null; then
      PRG="$link"
    else
      PRG="`dirname "$PRG"`/$link"
    fi
  done

  saveddir=`pwd`

  M2_HOME=`dirname "$PRG"`/..

  # make it fully qualified
  M2_HOME=`cd "$M2_HOME" && pwd`
Hugo
+18  A: 

readlink -f $path

pixelbeat
awesome, exactly what I needed!
rampion
This doesn't work in Mac OS X - see http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac
Bkkbrad
A: 

in the shell script above, what is the effect of the "-h" parameter in the while loop? If I google, "-h" only hits "help" information.

Thanks!

poseid
In the bash man page, under Conditional Expressions, -h means "True if file exists and is a symbolic link."
Greg Hewgill
+1  A: 

One of my favorites is realpath foo

realpath - return the canonicalized absolute pathname

realpath  expands  all  symbolic  links  and resolves references to '/./', '/../' and extra '/' characters in the null terminated string named by path and
       stores the canonicalized absolute pathname in the buffer of size PATH_MAX named by resolved_path.  The resulting path will have no symbolic link, '/./' or
       '/../' components.
Gregory
On Debian (etch and later) this command is available in the realpath package.
Phil Ross
+2  A: 

"pwd -P" seems to work if you just want the directory, but if for some reason you want the name of the actual executable I don't think that helps. Here's my solution:

#!/bin/bash

# get the absolute path of the executable
SELF_PATH=$(cd -P -- "$(dirname -- "$0")" && pwd -P) && SELF_PATH=$SELF_PATH/$(basename -- "$0")

# resolve symlinks
while [ -h $SELF_PATH ]; do
    # 1) cd to directory of the symlink
    # 2) cd to the directory of where the symlink points
    # 3) get the pwd
    # 4) append the basename
    DIR=$(dirname -- "$SELF_PATH")
    SYM=$(readlink $SELF_PATH)
    SELF_PATH=$(cd $DIR && cd $(dirname -- "$SYM") && pwd)/$(basename -- "$SYM")
done
tlrobinson
A: 

There's also a realpath function for Mac OS X:

http://codesnippets.joyent.com/posts/show/2049

Cheers,

gregx

A: 

Use readlink

http://blog.spikesource.com/readlink.htm

Vincech