예:
absolute="/foo/bar"
current="/foo/baz/foo"
# Magic
relative="../../bar"
마술을 만들려면 어떻게해야합니까?
답변
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
답변
이것은 @pini의 현재 최고 등급의 솔루션을 수정하여 완전히 기능적으로 개선 한 것입니다 (슬프게도 소수의 경우 만 처리).
알림 : 문자열이 길이가 0 인 경우 ‘-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"
답변
#!/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/}
답변
2001 년 부터 Perl에 내장되어 있으므로 VMS 까지 상상할 수있는 거의 모든 시스템에서 작동합니다 .
perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' FILE BASE
또한 솔루션은 이해하기 쉽습니다.
예를 들어,
perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' $absolute $current
… 잘 작동합니다.
답변
설치된 것으로 가정 : bash, pwd, dirname, echo; relpath는
#!/bin/bash
s=$(cd ${1%%/};pwd); d=$(cd $2;pwd); b=; while [ "${d#$s/}" == "${d}" ]
do s=$(dirname $s);b="../${b}"; done; echo ${b}${d#$s/}
나는 pini 의 대답을 골랐다. 와 다른 몇 가지 아이디어
참고 : 두 경로 모두 기존 폴더 여야합니다. 파일이 작동 하지 않습니다 .
답변
os.path.relpath
쉘 함수로서의 파이썬
이 relpath
연습 의 목표는 xni가os.path.relpath
제안한대로 Python 2.7의 기능 (Python 버전 2.6에서 사용 가능하지만 2.7에서만 제대로 작동 함)을 모방하는 것입니다. 입니다. 결과적으로 일부 결과는 다른 답변에서 제공되는 기능과 다를 수 있습니다.
( python -c
ZSH의 호출 을 기반으로 유효성 검사를 중단하기 때문에 단순히 경로에서 줄 바꿈으로 테스트하지 않았습니다 . 확실히 노력하면 가능합니다.)
Bash의 “매직”과 관련하여 나는 오래 전에 Bash에서 마법을 찾는 것을 포기했지만 이후 ZSH에서 필요한 모든 마법을 찾았습니다.
결과적으로 두 가지 구현을 제안합니다.
첫 번째 구현은 POSIX를 완전히 준수하는 것 입니다. /bin/dash
Debian 6.0.6“Squeeze”에서 테스트했습니다 . 또한 /bin/sh
OS X 10.8.3 에서도 완벽하게 작동합니다. 실제로 Bash 버전 3.2는 POSIX 쉘인 것처럼 보입니다.
두 번째 구현은 경로의 여러 슬래시 및 기타 방해 요소에 대비 한 ZSH 셸 함수입니다. ZSH를 사용할 수있는 경우 #!/usr/bin/env zsh
다른 쉘에서 아래에 제시된 스크립트 형식 (예 : shebang ) 으로 호출하더라도 권장 버전 입니다.
마지막으로 다른 답변에서 제공된 테스트 사례 relpath
에서 찾은 명령 의 출력을 확인하는 ZSH 스크립트를 작성했습니다 $PATH
. ! ? *
여기와 거기에 공백과 탭, 문장 부호를 추가하여 테스트에 향신료를 추가 하고 vim-powerline 에서 발견 된 이국적인 UTF-8 문자로 또 다른 테스트를 던졌습니다. .
POSIX 쉘 기능
먼저 POSIX 호환 쉘 기능. 다양한 경로에서 작동하지만 여러 개의 슬래시를 정리하거나 심볼릭 링크를 해결하지는 않습니다.
#!/bin/sh
relpath () {
[ $# -ge 1 ] && [ $# -le 2 ] || return 1
current="${2:+"$1"}"
target="${2:-"$1"}"
[ "$target" != . ] || target=/
target="/${target##/}"
[ "$current" != . ] || current=/
current="${current:="/"}"
current="/${current##/}"
appendix="${target##/}"
relative=''
while appendix="${target#"$current"/}"
[ "$current" != '/' ] && [ "$appendix" = "$target" ]; do
if [ "$current" = "$appendix" ]; then
relative="${relative:-.}"
echo "${relative#/}"
return 0
fi
current="${current%/*}"
relative="$relative${relative:+/}.."
done
relative="$relative${relative:+${appendix:+/}}${appendix#/}"
echo "$relative"
}
relpath "$@"
ZSH 쉘 기능
이제 더 강력한 zsh
버전입니다. 실제 경로 (la)에 대한 인수를 해결하려면 realpath -f
(Linux coreutils
패키지 에서 사용 가능 ) :a
3 행과 4 행을 다음과 같이 바꾸십시오 .:A
.
zsh에서 이것을 사용하려면 첫 번째와 마지막 행을 제거하고 $FPATH
변수 에있는 디렉토리에 넣으십시오 .
#!/usr/bin/env zsh
relpath () {
[[ $# -ge 1 ]] && [[ $# -le 2 ]] || return 1
local target=${${2:-$1}:a} # replace `:a' by `:A` to resolve symlinks
local current=${${${2:+$1}:-$PWD}:a} # replace `:a' by `:A` to resolve symlinks
local appendix=${target#/}
local relative=''
while appendix=${target#$current/}
[[ $current != '/' ]] && [[ $appendix = $target ]]; do
if [[ $current = $appendix ]]; then
relative=${relative:-.}
print ${relative#/}
return 0
fi
current=${current%/*}
relative="$relative${relative:+/}.."
done
relative+=${relative:+${appendix:+/}}${appendix#/}
print $relative
}
relpath "$@"
테스트 스크립트
마지막으로 테스트 스크립트 하나의 옵션, 즉 -v
상세 출력을 가능하게합니다.
#!/usr/bin/env zsh
set -eu
VERBOSE=false
script_name=$(basename $0)
usage () {
print "\n Usage: $script_name SRC_PATH DESTINATION_PATH\n" >&2
exit ${1:=1}
}
vrb () { $VERBOSE && print -P ${(%)@} || return 0; }
relpath_check () {
[[ $# -ge 1 ]] && [[ $# -le 2 ]] || return 1
target=${${2:-$1}}
prefix=${${${2:+$1}:-$PWD}}
result=$(relpath $prefix $target)
# Compare with python's os.path.relpath function
py_result=$(python -c "import os.path; print os.path.relpath('$target', '$prefix')")
col='%F{green}'
if [[ $result != $py_result ]] && col='%F{red}' || $VERBOSE; then
print -P "${col}Source: '$prefix'\nDestination: '$target'%f"
print -P "${col}relpath: ${(qq)result}%f"
print -P "${col}python: ${(qq)py_result}%f\n"
fi
}
run_checks () {
print "Running checks..."
relpath_check '/ a b/å/⮀*/!' '/ a b/å/⮀/xäå/?'
relpath_check '/' '/A'
relpath_check '/A' '/'
relpath_check '/ & / !/*/\\/E' '/'
relpath_check '/' '/ & / !/*/\\/E'
relpath_check '/ & / !/*/\\/E' '/ & / !/?/\\/E/F'
relpath_check '/X/Y' '/ & / !/C/\\/E/F'
relpath_check '/ & / !/C' '/A'
relpath_check '/A / !/C' '/A /B'
relpath_check '/Â/ !/C' '/Â/ !/C'
relpath_check '/ & /B / C' '/ & /B / C/D'
relpath_check '/ & / !/C' '/ & / !/C/\\/Ê'
relpath_check '/Å/ !/C' '/Å/ !/D'
relpath_check '/.A /*B/C' '/.A /*B/\\/E'
relpath_check '/ & / !/C' '/ & /D'
relpath_check '/ & / !/C' '/ & /\\/E'
relpath_check '/ & / !/C' '/\\/E/F'
relpath_check /home/part1/part2 /home/part1/part3
relpath_check /home/part1/part2 /home/part4/part5
relpath_check /home/part1/part2 /work/part6/part7
relpath_check /home/part1 /work/part1/part2/part3/part4
relpath_check /home /work/part2/part3
relpath_check / /work/part2/part3/part4
relpath_check /home/part1/part2 /home/part1/part2/part3/part4
relpath_check /home/part1/part2 /home/part1/part2/part3
relpath_check /home/part1/part2 /home/part1/part2
relpath_check /home/part1/part2 /home/part1
relpath_check /home/part1/part2 /home
relpath_check /home/part1/part2 /
relpath_check /home/part1/part2 /work
relpath_check /home/part1/part2 /work/part1
relpath_check /home/part1/part2 /work/part1/part2
relpath_check /home/part1/part2 /work/part1/part2/part3
relpath_check /home/part1/part2 /work/part1/part2/part3/part4
relpath_check home/part1/part2 home/part1/part3
relpath_check home/part1/part2 home/part4/part5
relpath_check home/part1/part2 work/part6/part7
relpath_check home/part1 work/part1/part2/part3/part4
relpath_check home work/part2/part3
relpath_check . work/part2/part3
relpath_check home/part1/part2 home/part1/part2/part3/part4
relpath_check home/part1/part2 home/part1/part2/part3
relpath_check home/part1/part2 home/part1/part2
relpath_check home/part1/part2 home/part1
relpath_check home/part1/part2 home
relpath_check home/part1/part2 .
relpath_check home/part1/part2 work
relpath_check home/part1/part2 work/part1
relpath_check home/part1/part2 work/part1/part2
relpath_check home/part1/part2 work/part1/part2/part3
relpath_check home/part1/part2 work/part1/part2/part3/part4
print "Done with checks."
}
if [[ $# -gt 0 ]] && [[ $1 = "-v" ]]; then
VERBOSE=true
shift
fi
if [[ $# -eq 0 ]]; then
run_checks
else
VERBOSE=true
relpath_check "$@"
fi
