Converting relative path into absolute path?

I'm not sure if these paths are duplicates. Given the relative path, how do I determine absolute path using a shell script?

Example:

relative path: /x/y/../../a/b/z/../c/d

absolute path: /a/b/c/d

Solution 1:

The most reliable method I've come across in unix is readlink -f:

$ readlink -f /x/y/../../a/b/z/../c/d
/a/b/c/d

A couple caveats:

  1. This also has the side-effect of resolving all symlinks. This may or may not be desirable, but usually is.
  2. readlink will give a blank result if you reference a non-existant directory. If you want to support non-existant paths, use readlink -m instead. Unfortunately this option doesn't exist on versions of readlink released before ~2005.

Solution 2:

From this source comes:

#!/bin/bash

# Assume parameter passed in is a relative path to a directory.
# For brevity, we won't do argument type or length checking.

ABS_PATH=`cd "$1"; pwd` # double quotes for paths that contain spaces etc...
echo "Absolute path: $ABS_PATH"

You can also do a Perl one-liner, e.g. using Cwd::abs_path

Solution 3:

Using bash

# Directory
relative_dir="folder/subfolder/"
absolute_dir="$( cd "$relative_dir" && pwd )"

# File
relative_file="folder/subfolder/file"
absolute_file="$( cd "${relative_file%/*}" && pwd )"/"${relative_file##*/}"
  • ${relative_file%/*} is same result as dirname "$relative_file"
  • ${relative_file##*/} is same result as basename "$relative_file"

Caveats: Does not resolve symbolic links (i.e. does not canonicalize path ) => May not differentiate all duplicates if you use symbolic links.


Using realpath

Command realpath does the job. An alternative is to use readlink -e (or readlink -f). However realpath is not often installed by default. If you cannot be sure realpath or readlink is present, you can substitute it using perl (see below).


Using perl

Steven Kramer proposes a shell alias if realpath is not available in your system:

$ alias realpath="perl -MCwd -e 'print Cwd::realpath(\$ARGV[0]),qq<\n>'"
$ realpath path/folder/file
/home/user/absolute/path/folder/file

or if you prefer using directly perl:

$ perl -MCwd -e 'print Cwd::realpath($ARGV[0]),qq<\n>' path/folder/file
/home/user/absolute/path/folder/file

This one-line perl command uses Cwd::realpath. There are in fact three perl functions. They take a single argument and return the absolute pathname. Below details are from documentation Perl5 > Core modules > Cwd.

  • abs_path() uses the same algorithm as getcwd(). Symbolic links and relative-path components (. and ..) are resolved to return the canonical pathname, just like realpath.

    use Cwd 'abs_path';
    my $abs_path = abs_path($file);
    
  • realpath() is a synonym for abs_path()

    use Cwd 'realpath';
    my $abs_path = realpath($file);
    
  • fast_abs_path() is a more dangerous, but potentially faster version of abs_path()

    use Cwd 'fast_abs_path';
    my $abs_path = fast_abs_path($file);
    

These functions are exported only on request => therefore use Cwd to avoid the "Undefined subroutine" error as pointed out by arielf. If you want to import all these three functions, you can use a single use Cwd line:

use Cwd qw(abs_path realpath fast_abs_path);