Never been to CodeSnippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world (or not, you can keep them private!)

realpath function for Mac OS X (See related posts)

An exercise in resolving symbolic links to full real paths (using /usr/bin/readlink).
For a much shorter version see the realpath function based on GNU readlink at the end.

Cf. also realpath command line utility for the Mac (C snippet; resolves Mac aliases as well) and Create & resolve Mac-type aliases from the command line.

function has_symlink() {

   declare basename path realpath

   path="${1}"   

   [[ "${path}" == '/' ]] && return 1

   #path="${path#/}"                     # trim a leading slash, if any
   #path="${path%/}"                     # trim a trailing slash, if any

   path="${path#"${path%%[!/]*}"}"       # trim leading slashes, if any
   path="${path%"${path##*[!/]}"}"       # trim trailing slashes, if any

   OIFS="${IFS}"
   IFS='/'

   for basename in $path; do
      realpath="${realpath%/}/${basename}"
      [[ -L "${realpath}" ]] && IFS="${OIFS}" && return 0
   done

   IFS="${OIFS}"

   return 1

}



function realpath() {

   declare basename currentpath optionstr path realpath
   declare -i var=0

   if [[ "${1}" == '-n' ]]; then optionstr='-n'; shift; fi


   # default to the current working directory
   # cf. help pwd; type -a pwd; type -p pwd; type -P pwd

   path="${1:-$(pwd -P)}"   


   if [[ "${path}" == '/' ]]; then 
      if [[ "${optionstr}" == '-n' ]]; then
         printf "%s" "${path}"
      else 
         printf "%s\n" "${path}"
      fi 
      return 0
   fi

   #path="${path#/}"                     # trim a leading slash, if any
   #path="${path%/}"                     # trim a trailing slash, if any

   path="${path#"${path%%[!/]*}"}"       # trim leading slashes, if any
   path="${path%"${path##*[!/]}"}"       # trim trailing slashes, if any


   OIFS="${IFS}"
   IFS='/'

   for basename in $path; do

      realpath="${realpath%/}/${basename}"

      if [[ "${realpath}" == '/' ]]; then break; fi

      realpath="${realpath%/}"

      # cf. http://www.commandlinefu.com/commands/view/2369/find-broken-symlinks-and-delete-them
      [[ -L "${realpath}" ]] && [[ -n "$(/usr/bin/find -L "${realpath}" -type l -maxdepth 0 -print 2>/dev/null)" ]] && echo "Broken symlink: ${realpath}" && return 1
      [[ ! -e "${realpath}" ]] && echo "No such file or directory: ${realpath}" && return 1
      [[ ! -L "${realpath}" ]] && realpath="${realpath}" && continue

      while [[ -L "${realpath}" ]]; do
         currentpath="${realpath}"
         realpath="$(/usr/bin/readlink "${realpath}")"
         [[ "${realpath}" == '/' ]] && continue
         realpath="${realpath%/}"
      done


: <<-'COMMENT'

      while [[ -L "${realpath}" ]]; do
         currentpath="${realpath}"
         if [[ -d  "${realpath}" ]]; then
            realpath="$(cd "${realpath}"; pwd -P)"
         else
            realpath="$(/usr/bin/readlink "${realpath}")"
         fi
         [[ "${realpath}" == '/' ]] && continue
         realpath="${realpath%/}"
      done

COMMENT

      currentpath="${currentpath%/*}"   # get dirname

      if [[ "${realpath}" == '/' ]]; then
         realpath='/'
      elif [[ "${realpath:0:1}" == '/' ]]; then
         realpath="${realpath}"
      else
         realpath="${currentpath}/${realpath}"
      fi

   done   # for

   IFS="${OIFS}"

   # repeat the procedure if $realpath has a symbolic link element in it
   if has_symlink "${realpath}"; then 
      if [[ "${optionstr}" == '-n' ]]; then
         realpath -n "${realpath}"
         var=1
      else 
         realpath "${realpath}"
         var=1
      fi 
   fi

   [[ $var -eq 1 ]] && return 0

   if [[ "${optionstr}" == '-n' ]]; then
      printf "%s" "${realpath}"
   else 
      printf "%s\n" "${realpath}"
   fi 

   return 0

}


realpath
realpath -n

cd /Volumes
realpath

cd /System/Library/Frameworks/Cocoa.framework/Headers
realpath

realpath /System/Library/Frameworks/Cocoa.framework/Headers/Cocoa.h


realpath /dev/stdin
realpath /dev/stdout
realpath /dev/stderr

realpath /network/Servers
realpath /network/Library


alias readlink='/usr/bin/readlink'
alias gnureadlink='/usr/local/bin/gnureadlink'


# cf. Compiling GNU coreutils on Mac OS X, 
# http://codesnippets.joyent.com/posts/show/1799

gnureadlink --help
gnureadlink -f /System/Library/Frameworks/Cocoa.framework/Headers/Cocoa.h
readlink /System/Library/Frameworks/Cocoa.framework/Headers/Cocoa.h
realpath /System/Library/Frameworks/Cocoa.framework/Headers/Cocoa.h


path='/System/Library/Frameworks/AddressBook.framework/AddressBook'
path='/System/Library/Frameworks/Automator.framework/Versions/Current/Resources/Dutch.lproj/InfoPlist.strings'
path="$(/usr/bin/find -x /Volumes -mindepth 1 -maxdepth 1 -type l -print | /usr/bin/head -n 1)"
path='/System/Library/Frameworks/AudioToolbox.framework/Versions/A/Headers/AudioToolbox.h'
path='/System/Library/Frameworks/AudioToolbox.framework/Versions/Current/Headers/AudioToolbox.h'


readlink "${path}"; echo $?
realpath "${path}"; echo $?
gnureadlink -f "${path}"; echo $?

realpath -n "${path}"



# realpath function based on GNU readlink
# cf. Compiling GNU coreutils on Mac OS X, 
# http://codesnippets.joyent.com/posts/show/1799

unset -f  realpath 
function realpath() {
   /usr/local/bin/gnureadlink -f "${1}"
   return 0
}

realpath "${path}"; echo $?

Comments on this post

gosselin53 posts on Oct 16, 2009 at 12:29
currentpath="${currentpath%/*}" # get dirname

if [[ "${realpath}" == '/' ]]; then
realpath='/'
elif [[ "${realpath:0:1}" == '/' ]]; then
realpath="${realpath}"
else
realpath="${currentpath}/${realpath}"
fi

done # for

IFS="${OIFS}"


Outdoor Media | Corporate Identity

You need to create an account or log in to post comments to this site.