Is there a Unix command to get the absolute (and canonicalized) path from a relative path which may contain symbolic links?
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
You can use the readlink utility, with the -f option:
-f, --canonicalizecanonicalize by following every symlink in every component of the given name recursively; all but the last component must exist
Some distributions, for example those that use GNU coreutils and FreeBSD, also come with a realpath(1) utility that basically just calls realpath(3) and does pretty much the same thing.
Method 2
Portably, the PWD variable is set by the shell to one absolute location of the current directory. Any component of that path may be a symbolic link.
case $f in /*) absolute=$f;; *) absolute=$PWD/$f;; esac
If you want to eliminate . and .. as well, change to the directory containing the file and obtain $PWD there:
if [ -d "$f" ]; then f=$f/.; fi
absolute=$(cd "$(dirname -- "$f")"; printf %s. "$PWD")
absolute=${absolute%?}
absolute=$absolute/${f##*/}
There’s no portable way to follow symbolic links. If you have a path to a directory, then on most unices $(cd -- "$dir" && pwd -P 2>/dev/null || pwd) provides a path that doesn’t use symbolic links, because shells that track symbolic links tend to implement pwd -P (“P” for “physical”).
Some unices provide a utility to print the “physical” path to a file.
- Reasonably recent Linux systems (with GNU coreutils or BusyBox) have
readlink -f, as do FreeBSD ≥8.3, NetBSD ≥4.0, and OpenBSD as far back as 2.2. - FreeBSD ≥4.3 has
realpath(it’s also present on some Linux systems, and it’s in BusyBox). -
If Perl is available, you can use the
Cwdmodule.perl -MCwd -e 'print Cwd::realpath($ARGV[0])' path/to/file
Method 3
Is pwd fit for your needs? It gives the absolute path of current directory. Or maybe what you want is realpath().
Method 4
alister and rss67 in this article introduce most stable, compatible and easiest way. I never seen better way than this before.
RELPATH=./../ cd $RELPATH ABSPATH=`pwd`
If you want to go back to the original location,
ORGPATH=`pwd` RELPATH=./../ cd $RELPATH ABSPATH=`pwd` cd $ORGPATH
or
RELPATH=./../ cd $RELPATH ABSPATH=`pwd` cd -
I wish this helps. This was greatest solution for me.
Method 5
The other answers here were fine but were insufficient for my needs. I needed a solution that I could use in my scripts on any machine. My solution was to write a shell script which I can invoke from the scripts where I need it.
#!/bin/sh
if [ $# -eq 0 ] || [ $# -gt 2 ]; then
printf 'Usage: respath path [working-directory]n' >&2
exit 1
fi
cantGoUp=
path=$1
if [ $# -gt 1 ]; then
cd "$2"
fi
cwd=`pwd -P` #Use -P option to account for directories that are actually symlinks
#Handle non-relative paths, which don't need resolution
if echo "$path" | grep '^/' > /dev/null ; then
printf '%sn' "$path"
exit 0
fi
#Resolve for each occurrence of ".." at the beginning of the given path.
#For performance, don't worry about ".." in the middle of the path.
while true
do
case "$path" in
..*)
if [ "$cwd" = '/' ]; then
printf 'Invalid relative pathn' >&2
exit 1
fi
if [ "$path" = '..' ]; then
path=
else
path=`echo "$path" | sed 's;^../;;'`
fi
cwd=`dirname $cwd`
;;
*)
break
;;
esac
done
cwd=`echo "$cwd" | sed 's;/$;;'`
if [ -z "$path" ]; then
if [ -z "$cwd" ]; then
cwd='/'
fi
printf '%sn' "$cwd"
else
printf '%s/%sn' "$cwd" "$path"
fi
This solution is written for the Bourne Shell for portability. I have this in a script named “respath.sh”.
This script can be used like this:
respath.sh '/path/to/file'
Or like this
respath.sh '/relative/path/here' '/path/to/resolve/relative/to'
The script resolves the path in the first argument using the path from the second argument as the starting point. If only the first argument is provided, then the script resolves the path relative to your current working directory.
Method 6
In case, additionally, you have some predefined path for $1 (usually ${PWD}), the following would work:
CWD="${1:-${PredefinedAbsolutePath}}"
if [ "${CWD}" != "${PredefinedAbsolutePath}}" ]; then
case $1 in
/*) echo "CWD=${CWD}"; ;;
*) CWD=$(realpath $1); echo "CWD=${CWD}"; ;;
esac
fi
Method 7
Respecting everyone else answer, I found the function below to be very easier and portable between linux / MAC OSX than others I found everywhere. Might help someone.
#!/usr/bin/bash
get_absolute_path(){
file_path=`pwd $1`
echo "$file_path/$1"
}
#to assign to a variable
absolute_path=$(get_absolute_path "file.txt")
echo $absolute_path
Method 8
Assume variable a stores a path name (a=”whatever”)
1. For potential space-containing path:
c=$(grep -o " " <<< $a | wc -l); if (( $c > "0" )); then a=$(sed 's/ /\ /g' <<< $a); fi
2. For the actual question, i.e. converting path to absolute: readlink can retrieve symlink contents, which can be employed as follows. (note readlink -f would have been perfect but is not available in at least Mac OS X bash, in which -f stands for FORMATS.)
if [[ $(readlink $a) == "" ]]; then a=$(cd $a; pwd); else a=$(cd $(readlink $a); pwd); fi
3. Just learned pwd -P in man pwd: -P is to “Display the physical current working directory (all symbolic links resolved)” and hence
if (( $(grep -o " " <<< $a | wc -l) > "0" )); then a=$(sed 's/ /\ /g' <<< $a); fi; a=$(cd $a; pwd -P)
shall work, too.
Conclusion: combine 1 with 2 to satisfy both of your requirements, while space-containing path names are already taken care of in the more concise 3.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0