A realpath Implementation in Bash ¬
2012-02-19
I was recently informed of an issue with the in-development version of trash
(one of the utilities in tools-osx) that required using an absolute path internally instead of a relative path. In most languages one can just run a path through realpath()
to get the absolute path for a relative path, but bash
(which trash
was developed in) doesn’t have an equivalent. Many people suggest readlink
, but it’s generally only included in Linux distributions, so BSD-based operating systems (incl. Mac OS X) are a bit out of luck.
Fortunately, it’s quite easy to emulate using pwd
, but there is a bit of extra work that must be done. I found a good, portable example, but it still wasn’t quite up to my standards. I’ve simplified & improved it and the result is as follows (last updated 2012-11-29):
function realpath() { local success=true local path="$1"
# make sure the string isn't empty as that implies something in further logic if [ -z "$path" ]; then success=false else # start with the file name (sans the trailing slash) path="${path%/}"
# if we stripped off the trailing slash and were left with nothing, that means we're in the root directory if [ -z "$path" ]; then path="/" fi
# get the basename of the file (ignoring '.' & '..', because they're really part of the path) local file_basename="${path##*/}" if [[ ( "$file_basename" = "." ) || ( "$file_basename" = ".." ) ]]; then file_basename="" fi
# extracts the directory component of the full path, if it's empty then assume '.' (the current working directory) local directory="${path%$file_basename}" if [ -z "$directory" ]; then directory='.' fi
# attempt to change to the directory if ! cd "$directory" &>/dev/null ; then success=false fi
if $success; then # does the filename exist? if [[ ( -n "$file_basename" ) && ( ! -e "$file_basename" ) ]]; then success=false fi
# get the absolute path of the current directory & change back to previous directory local abs_path="$(pwd -P)" cd "-" &>/dev/null
# Append base filename to absolute path if [ "${abs_path}" = "/" ]; then abs_path="${abs_path}${file_basename}" else abs_path="${abs_path}/${file_basename}" fi
# output the absolute path echo "$abs_path" fi fi
$success }
Here’s a simple example of its usage:
relative_path="../Shared/"
absolute_path="$(realpath "$relative_path")"
if [ $? -eq 0 ]; then
echo "absolute_path = $absolute_path"
else
echo "'$relative_path' does not exist!"
fi
I have also tossed together a realpath
tool which wraps around this implementation if you would like to install it for use in multiple scripts/tools. Feel free to download it from the development page or get the source code on GitHub. It’s released under a BSD license.