Using realpath from GNU coreutils 8.23 is the simplest, I think:
$ realpath --relative-to="$file1" "$file2"
For example:
$ realpath --relative-to=/usr/bin/nmap /tmp/testing ../../../tmp/testing
ID : 10008
viewed : 32
Tags : bashshellpathrelative-pathabsolute-pathbash
94
Using realpath from GNU coreutils 8.23 is the simplest, I think:
$ realpath --relative-to="$file1" "$file2"
For example:
$ realpath --relative-to=/usr/bin/nmap /tmp/testing ../../../tmp/testing
90
$ python -c "import os.path; print os.path.relpath('/foo/bar', '/foo/baz/foo')"
gives:
../../bar
70
This is a corrected, fully functional improvement of the currently best rated solution from @pini (which sadly handle only a few cases)
Reminder : '-z' test if the string is zero-length (=empty) and '-n' test if the string is not empty.
# both $1 and $2 are absolute paths beginning with / # returns relative path to $2/$target from $1/$source source=$1 target=$2 common_part=$source # for now result="" # for now while [[ "${target#$common_part}" == "${target}" ]]; do # no match, means that candidate common part is not correct # go up one level (reduce common part) common_part="$(dirname $common_part)" # and record that we went back, with correct / handling if [[ -z $result ]]; then result=".." else result="../$result" fi done if [[ $common_part == "/" ]]; then # special case for root (no common path) result="$result/" fi # since we now have identified the common part, # compute the non-common part forward_part="${target#$common_part}" # and now stick all parts together if [[ -n $result ]] && [[ -n $forward_part ]]; then result="$result$forward_part" elif [[ -n $forward_part ]]; then # extra slash removal result="${forward_part:1}" fi echo $result
Test cases :
compute_relative.sh "/A/B/C" "/A" --> "../.." compute_relative.sh "/A/B/C" "/A/B" --> ".." compute_relative.sh "/A/B/C" "/A/B/C" --> "" compute_relative.sh "/A/B/C" "/A/B/C/D" --> "D" compute_relative.sh "/A/B/C" "/A/B/C/D/E" --> "D/E" compute_relative.sh "/A/B/C" "/A/B/D" --> "../D" compute_relative.sh "/A/B/C" "/A/B/D/E" --> "../D/E" compute_relative.sh "/A/B/C" "/A/D" --> "../../D" compute_relative.sh "/A/B/C" "/A/D/E" --> "../../D/E" compute_relative.sh "/A/B/C" "/D/E/F" --> "../../../D/E/F"
68
#!/bin/bash # both $1 and $2 are absolute paths # returns $2 relative to $1 source=$1 target=$2 common_part=$source back= while [ "${target#$common_part}" = "${target}" ]; do common_part=$(dirname $common_part) back="../${back}" done echo ${back}${target#$common_part/}
55
It is built in to Perl since 2001, so it works on nearly every system you can imagine, even VMS.
perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' FILE BASE
Also, the solution is easy to understand.
So for your example:
perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' $absolute $current
...would work fine.