例子:

absolute="/foo/bar"
current="/foo/baz/foo"

# Magic

relative="../../bar"

我如何创造魔法(希望不是太复杂的代码…)?


当前回答

下面是一个shell脚本,它可以在不调用其他程序的情况下完成:

#! /bin/env bash 

#bash script to find the relative path between two directories

mydir=${0%/}
mydir=${0%/*}
creadlink="$mydir/creadlink"

shopt -s extglob

relpath_ () {
        path1=$("$creadlink" "$1")
        path2=$("$creadlink" "$2")
        orig1=$path1
        path1=${path1%/}/
        path2=${path2%/}/

        while :; do
                if test ! "$path1"; then
                        break
                fi
                part1=${path2#$path1}
                if test "${part1#/}" = "$part1"; then
                        path1=${path1%/*}
                        continue
                fi
                if test "${path2#$path1}" = "$path2"; then
                        path1=${path1%/*}
                        continue
                fi
                break
        done
        part1=$path1
        path1=${orig1#$part1}
        depth=${path1//+([^\/])/..}
        path1=${path2#$path1}
        path1=${depth}${path2#$part1}
        path1=${path1##+(\/)}
        path1=${path1%/}
        if test ! "$path1"; then
                path1=.
        fi
        printf "$path1"

}

relpath_test () {
        res=$(relpath_ /path1/to/dir1 /path1/to/dir2 )
        expected='../dir2'
        test_results "$res" "$expected"

        res=$(relpath_ / /path1/to/dir2 )
        expected='path1/to/dir2'
        test_results "$res" "$expected"

        res=$(relpath_ /path1/to/dir2 / )
        expected='../../..'
        test_results "$res" "$expected"

        res=$(relpath_ / / )
        expected='.'
        test_results "$res" "$expected"

        res=$(relpath_ /path/to/dir2/dir3 /path/to/dir1/dir4/dir4a )
        expected='../../dir1/dir4/dir4a'
        test_results "$res" "$expected"

        res=$(relpath_ /path/to/dir1/dir4/dir4a /path/to/dir2/dir3 )
        expected='../../../dir2/dir3'
        test_results "$res" "$expected"

        #res=$(relpath_ . /path/to/dir2/dir3 )
        #expected='../../../dir2/dir3'
        #test_results "$res" "$expected"
}

test_results () {
        if test ! "$1" = "$2"; then
                printf 'failed!\nresult:\nX%sX\nexpected:\nX%sX\n\n' "$@"
        fi
}

#relpath_test

来源:http://www.ynform.org/w/Pub/Relpath

其他回答

#!/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/}
$ python -c "import os.path; print os.path.relpath('/foo/bar', '/foo/baz/foo')"

给:

../../bar

使用GNU coreutils 8.23中的realpath是最简单的,我认为:

$ realpath --relative-to="$file1" "$file2"

例如:

$ realpath --relative-to=/usr/bin/nmap /tmp/testing
../../../tmp/testing

我猜这个也可以…(自带内置测试):)

好吧,预计会有一些开销,但我们在这里做的是伯恩壳!;)

#!/bin/sh

#
# Finding the relative path to a certain file ($2), given the absolute path ($1)
# (available here too http://pastebin.com/tWWqA8aB)
#
relpath () {
  local  FROM="$1"
  local    TO="`dirname  $2`"
  local  FILE="`basename $2`"
  local  DEBUG="$3"

  local FROMREL=""
  local FROMUP="$FROM"
  while [ "$FROMUP" != "/" ]; do
    local TOUP="$TO"
    local TOREL=""
    while [ "$TOUP" != "/" ]; do
      [ -z "$DEBUG" ] || echo 1>&2 "$DEBUG$FROMUP =?= $TOUP"
      if [ "$FROMUP" = "$TOUP" ]; then
        echo "${FROMREL:-.}/$TOREL${TOREL:+/}$FILE"
        return 0
      fi
      TOREL="`basename $TOUP`${TOREL:+/}$TOREL"
      TOUP="`dirname $TOUP`"
    done
    FROMREL="..${FROMREL:+/}$FROMREL"
    FROMUP="`dirname $FROMUP`"
  done
  echo "${FROMREL:-.}${TOREL:+/}$TOREL/$FILE"
  return 0
}

relpathshow () {
  echo " - target $2"
  echo "   from   $1"
  echo "   ------"
  echo "   => `relpath $1 $2 '      '`"
  echo ""
}

# If given 2 arguments, do as said...
if [ -n "$2" ]; then
  relpath $1 $2

# If only one given, then assume current directory
elif [ -n "$1" ]; then
  relpath `pwd` $1

# Otherwise perform a set of built-in tests to confirm the validity of the method! ;)
else

  relpathshow /usr/share/emacs22/site-lisp/emacs-goodies-el \
              /usr/share/emacs22/site-lisp/emacs-goodies-el/filladapt.el

  relpathshow /usr/share/emacs23/site-lisp/emacs-goodies-el \
              /usr/share/emacs22/site-lisp/emacs-goodies-el/filladapt.el

  relpathshow /usr/bin \
              /usr/share/emacs22/site-lisp/emacs-goodies-el/filladapt.el

  relpathshow /usr/bin \
              /usr/share/emacs22/site-lisp/emacs-goodies-el/filladapt.el

  relpathshow /usr/bin/share/emacs22/site-lisp/emacs-goodies-el \
              /etc/motd

  relpathshow / \
              /initrd.img
fi

test.sh:

#!/bin/bash                                                                 

cd /home/ubuntu
touch blah
TEST=/home/ubuntu/.//blah
echo TEST=$TEST
TMP=$(readlink -e "$TEST")
echo TMP=$TMP
REL=${TMP#$(pwd)/}
echo REL=$REL

测试:

$ ./test.sh 
TEST=/home/ubuntu/.//blah
TMP=/home/ubuntu/blah
REL=blah