Convert absolute path into relative path given a current directory using Bash Convert absolute path into relative path given a current directory using Bash bash bash

Convert absolute path into relative path given a current directory using Bash


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


$ python -c "import os.path; print os.path.relpath('/foo/bar', '/foo/baz/foo')"

gives:

../../bar


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/$sourcesource=$1target=$2common_part=$source # for nowresult="" # for nowwhile [[ "${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"    fidoneif [[ $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 partforward_part="${target#$common_part}"# and now stick all parts togetherif [[ -n $result ]] && [[ -n $forward_part ]]; then    result="$result$forward_part"elif [[ -n $forward_part ]]; then    # extra slash removal    result="${forward_part:1}"fiecho $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"