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

ID : 10008

viewed : 32

Tags : bashshellpathrelative-pathabsolute-pathbash

Top 5 Answer for shell - Convert absolute path into relative path given a current directory using Bash

vote vote

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 
vote vote

90

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

gives:

../../bar 
vote vote

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" 
vote vote

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/} 
vote vote

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.

Top 3 video Explaining shell - Convert absolute path into relative path given a current directory using Bash

Related QUESTION?