例子:
absolute="/foo/bar"
current="/foo/baz/foo"
# Magic
relative="../../bar"
我如何创造魔法(希望不是太复杂的代码…)?
例子:
absolute="/foo/bar"
current="/foo/baz/foo"
# Magic
relative="../../bar"
我如何创造魔法(希望不是太复杂的代码…)?
当前回答
我需要这样的东西,但它也解决了符号链接。我发现pwd有一个-P标志用于此目的。附加了我的脚本的一个片段。它在shell脚本的函数中,因此是$1和$2。结果值是从START_ABS到END_ABS的相对路径,位于UPDIRS变量中。为了执行pwd -P,将脚本cd放入每个参数目录,这也意味着将处理相对路径参数。干杯,吉姆
SAVE_DIR="$PWD"
cd "$1"
START_ABS=`pwd -P`
cd "$SAVE_DIR"
cd "$2"
END_ABS=`pwd -P`
START_WORK="$START_ABS"
UPDIRS=""
while test -n "${START_WORK}" -a "${END_ABS/#${START_WORK}}" '==' "$END_ABS";
do
START_WORK=`dirname "$START_WORK"`"/"
UPDIRS=${UPDIRS}"../"
done
UPDIRS="$UPDIRS${END_ABS/#${START_WORK}}"
cd "$SAVE_DIR"
其他回答
我猜这个也可以…(自带内置测试):)
好吧,预计会有一些开销,但我们在这里做的是伯恩壳!;)
#!/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
使用GNU coreutils 8.23中的realpath是最简单的,我认为:
$ realpath --relative-to="$file1" "$file2"
例如:
$ realpath --relative-to=/usr/bin/nmap /tmp/testing
../../../tmp/testing
$ python -c "import os.path; print os.path.relpath('/foo/bar', '/foo/baz/foo')"
给:
../../bar
kasku和Pini的答案略有改进,空格更好,允许传递相对路径:
#!/bin/bash
# both $1 and $2 are paths
# returns $2 relative to $1
absolute=`readlink -f "$2"`
current=`readlink -f "$1"`
# Perl is magic
# Quoting horror.... spaces cause problems, that's why we need the extra " in here:
relative=$(perl -MFile::Spec -e "print File::Spec->abs2rel(q($absolute),q($current))")
echo $relative
这是对@pini目前评分最高的解决方案(遗憾的是,它只处理少数情况)的更正,全功能改进
提醒:'-z'测试如果字符串是零长度(=空),'-n'测试如果字符串不是空。
# 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
测试用例:
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"