উদাহরণ:
absolute="/foo/bar"
current="/foo/baz/foo"
# Magic
relative="../../bar"
আমি কীভাবে যাদু তৈরি করব (আশা করি খুব জটিল কোড নয় ...)?
realpath --relative-to=$absolute $current
।
উদাহরণ:
absolute="/foo/bar"
current="/foo/baz/foo"
# Magic
relative="../../bar"
আমি কীভাবে যাদু তৈরি করব (আশা করি খুব জটিল কোড নয় ...)?
realpath --relative-to=$absolute $current
।
উত্তর:
জিএনইউ কোর্টিলস ৮.২৩ থেকে রিয়েলপথ ব্যবহার করা সবচেয়ে সহজ, আমি মনে করি:
$ realpath --relative-to="$file1" "$file2"
উদাহরণ স্বরূপ:
$ realpath --relative-to=/usr/bin/nmap /tmp/testing
../../../tmp/testing
$ realpath --relative-to="${PWD}" "$file"
আপনি যদি বর্তমান ওয়ার্কিং ডিরেক্টরিটির সাথে সম্পর্কিত পাথগুলি চান তা কার্যকর।
/usr/bin/nmap/
-path কিন্তু জন্য /usr/bin/nmap
থেকে: nmap
থেকে /tmp/testing
এটা শুধুমাত্র ../../
এবং 3 বার ../
। এটি তবে কাজ করে, কারণ ..
rootfs এ করা /
।
--relative-to=…
একটি ডিরেক্টরি আশা করে এবং চেক করে না। এর অর্থ হ'ল আপনি অতিরিক্ত "../" দিয়ে শেষ করেছেন যদি আপনি কোনও ফাইলের সাথে সম্পর্কিত কোনও পাথের জন্য অনুরোধ করেন (উদাহরণ হিসাবে এটি প্রদর্শিত হয়, কারণ /usr/bin
খুব কমই বা কখনই ডিরেক্টরি থাকে না এবং nmap
সাধারণত একটি বাইনারি হয়)
$ python -c "import os.path; print os.path.relpath('/foo/bar', '/foo/baz/foo')"
দেয়:
../../bar
relpath(){ python -c "import os.path; print os.path.relpath('$1','${2:-$PWD}')" ; }
python -c 'import os, sys; print(os.path.relpath(*sys.argv[1:]))'
সবচেয়ে প্রাকৃতিক এবং নির্ভরযোগ্যভাবে কাজ করে।
এটি @ পিনি থেকে বর্তমানে সর্বাধিক নির্ধারিত সমাধানটির একটি সংশোধনযোগ্য, সম্পূর্ণ ক্রিয়ামূলক উন্নতি (যা দুঃখের সাথে কেবল কয়েকটি ক্ষেত্রে পরিচালিত হয়)
অনুস্মারক: স্ট্রিং শূন্য দৈর্ঘ্য (= খালি) হলে '-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"
source=$1; target=$2
সঙ্গেsource=$(realpath $1); target=$(realpath $2)
realpath
সুপারিশ করা হয়, source=$(readlink -f $1)
ইত্যাদি ইত্যাদি যদি রিয়েলপথ না পাওয়া যায় (মানসম্মত নয়)
$source
এবং এর $target
মতো: `যদি [[-e $ 1]]; তারপরে উত্স = $ (রিডলিঙ্ক -f f 1); অন্য উত্স = $ 1; ফাই যদি [[-e $ 2]]; তারপরে টার্গেট = read (রিডলিঙ্ক -f $ 2); অন্য লক্ষ্য = $ 2; fi` এইভাবে, ফাংশনটি সত্যিকারের / বিদ্যমান আপেক্ষিক পাথের পাশাপাশি কাল্পনিক ডিরেক্টরিগুলিকে হ্যান্ডেল করতে পারে।
readlink
যে একটি -m
বিকল্প রয়েছে যা ঠিক তা করে;)
#!/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/}
এটি ২০০১ সাল থেকে পারলে অন্তর্নির্মিত , তাই এটি আপনি কল্পনা করতে পারেন এমন প্রায় প্রতিটি সিস্টেমে এমনকি ভিএমএসেও কাজ করে ।
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
... ভাল কাজ করবে।
say
লগ হিসাবে পার্লে পাওয়া যায় নি, তবে এটি কার্যকরভাবে এখানে ব্যবহার করা যেতে পারে। perl -MFile::Spec -E 'say File::Spec->abs2rel(@ARGV)'
perl -MFile::Spec -e 'print File::Spec->abs2rel(@ARGV)' "$target"
এবং perl -MFile::Spec -e 'print File::Spec->abs2rel(@ARGV)' "$target" "$origin"
। প্রথম এক-লাইন পার্ল স্ক্রিপ্টটিতে একটি আর্গুমেন্ট ব্যবহার করা হয় (মূলটি বর্তমান কার্যক্ষম ডিরেক্টরি)। দ্বিতীয় এক-লাইনের পার্ল স্ক্রিপ্ট দুটি আর্গুমেন্ট ব্যবহার করে।
perl
উত্তরটি এখনও এক-রেখাযুক্ত হলেও প্রায় সর্বত্রই পাওয়া যাবে।
ধরে নেই যে আপনি ইনস্টল করেছেন: বাশ, পিডব্লিউডি, ডারনাম, ইকো; তাহলে রেলপথ হয় is
#!/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/}
আমি পিনি এবং অন্যান্য কয়েকটি ধারণার উত্তর গল্ফ করেছি
দ্রষ্টব্য : বিদ্যমান ফোল্ডারগুলির জন্য এটি উভয় পাথের প্রয়োজন। ফাইলগুলি কাজ করবে না ।
os.path.relpath
শেল ফাংশন হিসাবেএই relpath
অনুশীলনের লক্ষ্যটি পাইথন ২.7 এর os.path.relpath
ক্রিয়াকলাপটি নকল করা (পাইথন সংস্করণ ২.6 থেকে পাওয়া তবে কেবল ২. 2.-তে সঠিকভাবে কাজ করা), এক্সনি দ্বারা প্রস্তাবিত হিসাবে । ফলস্বরূপ, কিছু ফলাফল অন্যান্য উত্তরে প্রদত্ত ফাংশন থেকে পৃথক হতে পারে।
(আমি python -c
জাস্টএসএইচ থেকে কল করার উপর ভিত্তি করে বৈধতা ভঙ্গ করেছি বলে পাথগুলিতে নতুন লাইনের সাথে পরীক্ষা করিনি । কিছু প্রচেষ্টা দিয়ে অবশ্যই এটি সম্ভব হবে))
বাশ-এ "যাদু" সম্পর্কে, আমি অনেক আগে বাশে ম্যাজিক সন্ধান করা ছেড়ে দিয়েছি, তবে আমি তখন থেকে আমার প্রয়োজনীয় সমস্ত যাদু এবং পরে কিছু জেডএসএইচে খুঁজে পেয়েছি in
ফলস্বরূপ, আমি দুটি বাস্তবায়ন প্রস্তাব।
প্রথম প্রয়োগটি সম্পূর্ণরূপে পসিক্স-সম্মতিযুক্ত হওয়ার লক্ষ্য । আমি এটি /bin/dash
ডেবিয়ান 6.0.6 "চেপে ধরুন" দিয়ে পরীক্ষা করেছি । এটি /bin/sh
ওএস এক্স ১০.৮.৩ এ পুরোপুরি কাজ করে যা আসলে পসিক্স শেল বলে ভান করে বাশ সংস্করণ ৩.২।
দ্বিতীয় বাস্তবায়নটি একটি জেডএসএইচ শেল ফাংশন যা একাধিক স্ল্যাশ এবং পাথের অন্যান্য উপদ্রবগুলির বিরুদ্ধে শক্ত। যদি আপনার কাছে জেডএসএইচ উপলব্ধ থাকে তবে এটি প্রস্তাবিত সংস্করণ, এমনকি যদি আপনি #!/usr/bin/env zsh
অন্য শেল থেকে নীচে উপস্থাপিত (যেমন একটি শেবাং দিয়ে ) উপস্থাপিত স্ক্রিপ্ট আকারে কল করে থাকেন ।
অবশেষে, আমি একটি জেডএসএইচ স্ক্রিপ্ট লিখেছি যা অন্যান্য উত্তরে প্রদত্ত পরীক্ষার ক্ষেত্রে প্রদত্ত relpath
কমান্ডের আউটপুট যাচাই করে $PATH
। আমি এই পরীক্ষাগুলিতে কিছু স্পেস, ট্যাব এবং বিরামচিহ্ন যুক্ত করে যেমন ! ? *
এখানে এবং সেখানে যুক্ত করে কিছু মশলা যুক্ত করেছি এবং ভিআইএম-পাওয়ারলাইনে পাওয়া বিদেশী ইউটিএফ -8 অক্ষরের সাথে আরও একটি পরীক্ষায় ফেলেছি ।
প্রথমত, পসিক্স-কমপ্লায়েন্ট শেল ফাংশন। এটি বিভিন্ন পাথের সাথে কাজ করে তবে একাধিক স্ল্যাশ পরিষ্কার করে না বা সিমলিংকগুলি সমাধান করে না।
#!/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
সংস্করণ। যদি আপনি এটি সত্যিকারের পাথ realpath -f
(লিনাক্স 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
/
আমি ভয়ে কাজ করি না।
#!/bin/sh
# Return relative path from canonical absolute dir path $1 to canonical
# absolute dir path $2 ($1 and/or $2 may end with one or no "/").
# Does only need POSIX shell builtins (no external command)
relPath () {
local common path up
common=${1%/} path=${2%/}/
while test "${path#"$common"/}" = "$path"; do
common=${common%/*} up=../$up
done
path=$up${path#"$common"/}; path=${path%/}; printf %s "${path:-.}"
}
# Return relative path from dir $1 to dir $2 (Does not impose any
# restrictions on $1 and $2 but requires GNU Core Utility "readlink"
# HINT: busybox's "readlink" does not support option '-m', only '-f'
# which requires that all but the last path component must exist)
relpath () { relPath "$(readlink -m "$1")" "$(readlink -m "$2")"; }
শেল স্ক্রিপ্টের উপরে পিনির (ধন্যবাদ!) দ্বারা অনুপ্রাণিত হয়েছিল । এটি স্ট্যাক ওভারফ্লো (কমপক্ষে আমার পূর্বরূপ ফ্রেমে) সিনট্যাক্স হাইলাইটিং মডিউলে একটি বাগ ট্রিগার করে। তাই হাইলাইটিং ভুল হলে দয়া করে এড়িয়ে যান।
কিছু নোট:
উল্লিখিত ব্যাকস্ল্যাশ সিকোয়েন্স বাদে ফাংশন "রিলপাথ" এর শেষ লাইনটি পাইথনের সাথে সামঞ্জস্যপূর্ণ পথের নামগুলি আউটপুট করে:
path=$up${path#"$common"/}; path=${path%/}; printf %s "${path:-.}"
শেষ লাইনটি রেখার দ্বারা প্রতিস্থাপন করা যেতে পারে (এবং সরলীকৃত)
printf %s "$up${path#"$common"/}"
আমি পরেরটি পছন্দ করি কারণ
ফাইলের নামগুলি রিলপ্যাথ দ্বারা প্রাপ্ত দির পাথগুলিতে সরাসরি যুক্ত করা যেতে পারে, যেমন:
ln -s "$(relpath "<fromDir>" "<toDir>")<file>" "<fromDir>"
এই পদ্ধতিতে তৈরি একই দিরের সিম্বলিক লিঙ্কগুলিতে "./"
ফাইলের নামটিতে কুরুচিপূর্ণ প্রেন্টেন্ট নেই।
রিগ্রেশন পরীক্ষার জন্য কোড তালিকা (কেবল শেল স্ক্রিপ্টে এটি যুক্ত করুন):
############################################################################
# If called with 2 arguments assume they are dir paths and print rel. path #
############################################################################
test "$#" = 2 && {
printf '%s\n' "Rel. path from '$1' to '$2' is '$(relpath "$1" "$2")'."
exit 0
}
#######################################################
# If NOT called with 2 arguments run regression tests #
#######################################################
format="\t%-19s %-22s %-27s %-8s %-8s %-8s\n"
printf \
"\n\n*** Testing own and python's function with canonical absolute dirs\n\n"
printf "$format\n" \
"From Directory" "To Directory" "Rel. Path" "relPath" "relpath" "python"
IFS=
while read -r p; do
eval set -- $p
case $1 in '#'*|'') continue;; esac # Skip comments and empty lines
# q stores quoting character, use " if ' is used in path name
q="'"; case $1$2 in *"'"*) q='"';; esac
rPOk=passed rP=$(relPath "$1" "$2"); test "$rP" = "$3" || rPOk=$rP
rpOk=passed rp=$(relpath "$1" "$2"); test "$rp" = "$3" || rpOk=$rp
RPOk=passed
RP=$(python -c "import os.path; print os.path.relpath($q$2$q, $q$1$q)")
test "$RP" = "$3" || RPOk=$RP
printf \
"$format" "$q$1$q" "$q$2$q" "$q$3$q" "$q$rPOk$q" "$q$rpOk$q" "$q$RPOk$q"
done <<-"EOF"
# From directory To directory Expected relative path
'/' '/' '.'
'/usr' '/' '..'
'/usr/' '/' '..'
'/' '/usr' 'usr'
'/' '/usr/' 'usr'
'/usr' '/usr' '.'
'/usr/' '/usr' '.'
'/usr' '/usr/' '.'
'/usr/' '/usr/' '.'
'/u' '/usr' '../usr'
'/usr' '/u' '../u'
"/u'/dir" "/u'/dir" "."
"/u'" "/u'/dir" "dir"
"/u'/dir" "/u'" ".."
"/" "/u'/dir" "u'/dir"
"/u'/dir" "/" "../.."
"/u'" "/u'" "."
"/" "/u'" "u'"
"/u'" "/" ".."
'/u"/dir' '/u"/dir' '.'
'/u"' '/u"/dir' 'dir'
'/u"/dir' '/u"' '..'
'/' '/u"/dir' 'u"/dir'
'/u"/dir' '/' '../..'
'/u"' '/u"' '.'
'/' '/u"' 'u"'
'/u"' '/' '..'
'/u /dir' '/u /dir' '.'
'/u ' '/u /dir' 'dir'
'/u /dir' '/u ' '..'
'/' '/u /dir' 'u /dir'
'/u /dir' '/' '../..'
'/u ' '/u ' '.'
'/' '/u ' 'u '
'/u ' '/' '..'
'/u\n/dir' '/u\n/dir' '.'
'/u\n' '/u\n/dir' 'dir'
'/u\n/dir' '/u\n' '..'
'/' '/u\n/dir' 'u\n/dir'
'/u\n/dir' '/' '../..'
'/u\n' '/u\n' '.'
'/' '/u\n' 'u\n'
'/u\n' '/' '..'
'/ a b/å/⮀*/!' '/ a b/å/⮀/xäå/?' '../../⮀/xäå/?'
'/' '/A' 'A'
'/A' '/' '..'
'/ & / !/*/\\/E' '/' '../../../../..'
'/' '/ & / !/*/\\/E' ' & / !/*/\\/E'
'/ & / !/*/\\/E' '/ & / !/?/\\/E/F' '../../../?/\\/E/F'
'/X/Y' '/ & / !/C/\\/E/F' '../../ & / !/C/\\/E/F'
'/ & / !/C' '/A' '../../../A'
'/A / !/C' '/A /B' '../../B'
'/Â/ !/C' '/Â/ !/C' '.'
'/ & /B / C' '/ & /B / C/D' 'D'
'/ & / !/C' '/ & / !/C/\\/Ê' '\\/Ê'
'/Å/ !/C' '/Å/ !/D' '../D'
'/.A /*B/C' '/.A /*B/\\/E' '../\\/E'
'/ & / !/C' '/ & /D' '../../D'
'/ & / !/C' '/ & /\\/E' '../../\\/E'
'/ & / !/C' '/\\/E/F' '../../../\\/E/F'
'/home/p1/p2' '/home/p1/p3' '../p3'
'/home/p1/p2' '/home/p4/p5' '../../p4/p5'
'/home/p1/p2' '/work/p6/p7' '../../../work/p6/p7'
'/home/p1' '/work/p1/p2/p3/p4' '../../work/p1/p2/p3/p4'
'/home' '/work/p2/p3' '../work/p2/p3'
'/' '/work/p2/p3/p4' 'work/p2/p3/p4'
'/home/p1/p2' '/home/p1/p2/p3/p4' 'p3/p4'
'/home/p1/p2' '/home/p1/p2/p3' 'p3'
'/home/p1/p2' '/home/p1/p2' '.'
'/home/p1/p2' '/home/p1' '..'
'/home/p1/p2' '/home' '../..'
'/home/p1/p2' '/' '../../..'
'/home/p1/p2' '/work' '../../../work'
'/home/p1/p2' '/work/p1' '../../../work/p1'
'/home/p1/p2' '/work/p1/p2' '../../../work/p1/p2'
'/home/p1/p2' '/work/p1/p2/p3' '../../../work/p1/p2/p3'
'/home/p1/p2' '/work/p1/p2/p3/p4' '../../../work/p1/p2/p3/p4'
'/-' '/-' '.'
'/?' '/?' '.'
'/??' '/??' '.'
'/???' '/???' '.'
'/?*' '/?*' '.'
'/*' '/*' '.'
'/*' '/**' '../**'
'/*' '/***' '../***'
'/*.*' '/*.**' '../*.**'
'/*.???' '/*.??' '../*.??'
'/[]' '/[]' '.'
'/[a-z]*' '/[0-9]*' '../[0-9]*'
EOF
format="\t%-19s %-22s %-27s %-8s %-8s\n"
printf "\n\n*** Testing own and python's function with arbitrary dirs\n\n"
printf "$format\n" \
"From Directory" "To Directory" "Rel. Path" "relpath" "python"
IFS=
while read -r p; do
eval set -- $p
case $1 in '#'*|'') continue;; esac # Skip comments and empty lines
# q stores quoting character, use " if ' is used in path name
q="'"; case $1$2 in *"'"*) q='"';; esac
rpOk=passed rp=$(relpath "$1" "$2"); test "$rp" = "$3" || rpOk=$rp
RPOk=passed
RP=$(python -c "import os.path; print os.path.relpath($q$2$q, $q$1$q)")
test "$RP" = "$3" || RPOk=$RP
printf "$format" "$q$1$q" "$q$2$q" "$q$3$q" "$q$rpOk$q" "$q$RPOk$q"
done <<-"EOF"
# From directory To directory Expected relative path
'usr/p1/..//./p4' 'p3/../p1/p6/.././/p2' '../../p1/p2'
'./home/../../work' '..//././../dir///' '../../dir'
'home/p1/p2' 'home/p1/p3' '../p3'
'home/p1/p2' 'home/p4/p5' '../../p4/p5'
'home/p1/p2' 'work/p6/p7' '../../../work/p6/p7'
'home/p1' 'work/p1/p2/p3/p4' '../../work/p1/p2/p3/p4'
'home' 'work/p2/p3' '../work/p2/p3'
'.' 'work/p2/p3' 'work/p2/p3'
'home/p1/p2' 'home/p1/p2/p3/p4' 'p3/p4'
'home/p1/p2' 'home/p1/p2/p3' 'p3'
'home/p1/p2' 'home/p1/p2' '.'
'home/p1/p2' 'home/p1' '..'
'home/p1/p2' 'home' '../..'
'home/p1/p2' '.' '../../..'
'home/p1/p2' 'work' '../../../work'
'home/p1/p2' 'work/p1' '../../../work/p1'
'home/p1/p2' 'work/p1/p2' '../../../work/p1/p2'
'home/p1/p2' 'work/p1/p2/p3' '../../../work/p1/p2/p3'
'home/p1/p2' 'work/p1/p2/p3/p4' '../../../work/p1/p2/p3/p4'
EOF
এখানে প্রচুর উত্তর প্রতিদিন ব্যবহারের জন্য ব্যবহারিক নয়। খাঁটি বাশে সঠিকভাবে এটি করা খুব কঠিন বিধায় আমি নিম্নলিখিত, নির্ভরযোগ্য সমাধানটি (একটি মন্তব্যে সমাহিত একটি পরামর্শের অনুরূপ) পরামর্শ দিচ্ছি:
function relpath() {
python -c "import os,sys;print(os.path.relpath(*(sys.argv[1:])))" "$@";
}
তারপরে, আপনি বর্তমান ডিরেক্টরি অনুসারে আপেক্ষিক পাথ পেতে পারেন:
echo $(relpath somepath)
বা আপনি নির্দিষ্ট করতে পারেন যে পথটি কোনও প্রদত্ত ডিরেক্টরি সম্পর্কিত relative
echo $(relpath somepath /etc) # relative to /etc
এর একটি অসুবিধার জন্য এটি অজগর দরকার, তবে:
মনে রাখবেন যে সমাধানগুলি অন্তর্ভুক্ত রয়েছে basename
বা dirname
অগত্যা নাও হতে পারে সেগুলি আরও ভাল হওয়া উচিত, কারণ তাদের coreutils
এটি ইনস্টল করা প্রয়োজন। কারও কাছে যদি বিশুদ্ধ bash
সমাধান থাকে যা নির্ভরযোগ্য এবং সহজ (বরং একটি সংশ্লেষিত কৌতূহল নয়), আমি অবাক হয়ে যাব।
এই স্ক্রিপ্টটি শুধুমাত্র ইনপুট ছাড়া পরম পথ বা আপেক্ষিক পাথ জন্য সঠিক ফলাফল দেয় .
বা ..
:
#!/bin/bash
# usage: relpath from to
if [[ "$1" == "$2" ]]
then
echo "."
exit
fi
IFS="/"
current=($1)
absolute=($2)
abssize=${#absolute[@]}
cursize=${#current[@]}
while [[ ${absolute[level]} == ${current[level]} ]]
do
(( level++ ))
if (( level > abssize || level > cursize ))
then
break
fi
done
for ((i = level; i < cursize; i++))
do
if ((i > level))
then
newpath=$newpath"/"
fi
newpath=$newpath".."
done
for ((i = level; i < abssize; i++))
do
if [[ -n $newpath ]]
then
newpath=$newpath"/"
fi
newpath=$newpath${absolute[i]}
done
echo "$newpath"
আমি কেবল পার্লটি এই অতি-তুচ্ছ কাজের জন্য ব্যবহার করব:
absolute="/foo/bar"
current="/foo/baz/foo"
# Perl is magic
relative=$(perl -MFile::Spec -e 'print File::Spec->abs2rel("'$absolute'","'$current'")')
perl -MFile::Spec -e "print File::Spec->abs2rel('$absolute','$current')"
যাতে পরম এবং বর্তমানের উদ্ধৃতি দেওয়া হয়।
relative=$(perl -MFile::Spec -e 'print File::Spec->abs2rel(@ARGV)' "$absolute" "$current")
। এটি নিশ্চিত করে যে মানগুলি পার্ল কোড থাকতে পারে না!
উপর সামান্য উন্নতি 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
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
readlink
উপর $(pwd)
।
অন্য একটি সমাধান, নিম্নলিখিত প্রসঙ্গে সহজ ব্যবহারের জন্য খাঁটি bash
+ জিএনইউ readlink
:
ln -s "$(relpath "$A" "$B")" "$B"
সম্পাদনা করুন: নিশ্চিত হয়ে নিন যে "$ বি" হয় হয় না বা সেই ক্ষেত্রে কোনও সফটলিঙ্ক নেই, অন্যথায়
relpath
এই লিঙ্কটি অনুসরণ করে যা আপনি চান তা নয়!
এটি প্রায় সমস্ত বর্তমান লিনাক্সে কাজ করে। যদি readlink -m
আপনার পক্ষে কাজ না করে তবে readlink -f
পরিবর্তে চেষ্টা করুন। সম্ভাব্য আপডেটের জন্য https://gist.github.com/hilbix/1ec361d00a8178ae8ea0 দেখুন :
: relpath A B
# Calculate relative path from A to B, returns true on success
# Example: ln -s "$(relpath "$A" "$B")" "$B"
relpath()
{
local X Y A
# We can create dangling softlinks
X="$(readlink -m -- "$1")" || return
Y="$(readlink -m -- "$2")" || return
X="${X%/}/"
A=""
while Y="${Y%/*}"
[ ".${X#"$Y"/}" = ".$X" ]
do
A="../$A"
done
X="$A${X#"$Y"/}"
X="${X%/}"
echo "${X:-.}"
}
মন্তব্য:
*
বা ক্ষেত্রে, অবাঞ্ছিত শেল মেটা চরিত্রের বিস্তারের বিরুদ্ধে সুরক্ষিত ?
।ln -s
:
relpath / /
দেয় .
এবং খালি স্ট্রিং নাrelpath a a
দেয় a
, এমনকি যদি a
একটি ডিরেক্টরি হতে পারেreadlink
হয় , সুতরাং পাথগুলিকে আধ্যাত্মিক করতে প্রয়োজন।readlink -m
এটির জন্য ধন্যবাদ এখনও বিদ্যমান পাথের জন্য কাজ করে না।পুরানো সিস্টেমে, যেখানে readlink -m
উপলব্ধ নেই, readlink -f
সেখানে ফাইলটি না থাকলে ব্যর্থ হয়। সুতরাং আপনার সম্ভবত সম্ভবত এর মতো কিছু কাজ করা উচিত (অনির্ধারিত!):
readlink_missing()
{
readlink -m -- "$1" && return
readlink -f -- "$1" && return
[ -e . ] && echo "$(readlink_missing "$(dirname "$1")")/$(basename "$1")"
}
এটি $1
অন্তর্ভুক্ত .
বা ..
অস্তিত্বহীন পাথের ক্ষেত্রে (যেমন পছন্দ করে /doesnotexist/./a
) ক্ষেত্রে সত্যই সঠিক নয় তবে এটি বেশিরভাগ ক্ষেত্রেই আবশ্যক।
( readlink -m --
উপরে দ্বারা প্রতিস্থাপন readlink_missing
।)
এখানে একটি পরীক্ষা দেওয়া হচ্ছে যে এই ফাংশনটি সত্যই সঠিক:
check()
{
res="$(relpath "$2" "$1")"
[ ".$res" = ".$3" ] && return
printf ':WRONG: %-10q %-10q gives %q\nCORRECT %-10q %-10q gives %q\n' "$1" "$2" "$res" "$@"
}
# TARGET SOURCE RESULT
check "/A/B/C" "/A" ".."
check "/A/B/C" "/A.x" "../../A.x"
check "/A/B/C" "/A/B" "."
check "/A/B/C" "/A/B/C" "C"
check "/A/B/C" "/A/B/C/D" "C/D"
check "/A/B/C" "/A/B/C/D/E" "C/D/E"
check "/A/B/C" "/A/B/D" "D"
check "/A/B/C" "/A/B/D/E" "D/E"
check "/A/B/C" "/A/D" "../D"
check "/A/B/C" "/A/D/E" "../D/E"
check "/A/B/C" "/D/E/F" "../../D/E/F"
check "/foo/baz/moo" "/foo/bar" "../bar"
গোলমেলে? ঠিক আছে, এগুলি সঠিক ফলাফল ! এমনকি যদি আপনি মনে করেন এটি প্রশ্নটির সাথে খাপ খায় না, তবে এখানে প্রমাণটি এটি সঠিক:
check "http://example.com/foo/baz/moo" "http://example.com/foo/bar" "../bar"
কোনও সন্দেহ ছাড়াই ../bar
পৃষ্ঠা bar
থেকে দেখা পৃষ্ঠার সঠিক এবং একমাত্র সঠিক আপেক্ষিক পথটি moo
। অন্য সব কিছুই সরল ভুল হবে।
আপাতদৃষ্টিতে ধরে নেওয়া প্রশ্নটি আউটপুট গ্রহণ করা তুচ্ছ, এটি current
একটি ডিরেক্টরি:
absolute="/foo/bar"
current="/foo/baz/foo"
relative="../$(relpath "$absolute" "$current")"
এটি ঠিক ফিরে আসে, যা চেয়েছিল।
এবং ভ্রু বাড়ানোর আগে এখানে কিছুটা জটিল রূপ relpath
(ছোট পার্থক্যটি চিহ্নিত করুন), যা ইউআরএল-সিনট্যাক্সের জন্যও কাজ করা উচিত (সুতরাং /
কিছু পিছনে টিকে আছে, কিছু- bash
ম্যাজিকের জন্য ধন্যবাদ ):
# Calculate relative PATH to the given DEST from the given BASE
# In the URL case, both URLs must be absolute and have the same Scheme.
# The `SCHEME:` must not be present in the FS either.
# This way this routine works for file paths an
: relpathurl DEST BASE
relpathurl()
{
local X Y A
# We can create dangling softlinks
X="$(readlink -m -- "$1")" || return
Y="$(readlink -m -- "$2")" || return
X="${X%/}/${1#"${1%/}"}"
Y="${Y%/}${2#"${2%/}"}"
A=""
while Y="${Y%/*}"
[ ".${X#"$Y"/}" = ".$X" ]
do
A="../$A"
done
X="$A${X#"$Y"/}"
X="${X%/}"
echo "${X:-.}"
}
এবং এখানে চেকগুলি কেবল পরিষ্কার করার জন্য রয়েছে: এটি সত্যিই বলা হিসাবে কাজ করে।
check()
{
res="$(relpathurl "$2" "$1")"
[ ".$res" = ".$3" ] && return
printf ':WRONG: %-10q %-10q gives %q\nCORRECT %-10q %-10q gives %q\n' "$1" "$2" "$res" "$@"
}
# TARGET SOURCE RESULT
check "/A/B/C" "/A" ".."
check "/A/B/C" "/A.x" "../../A.x"
check "/A/B/C" "/A/B" "."
check "/A/B/C" "/A/B/C" "C"
check "/A/B/C" "/A/B/C/D" "C/D"
check "/A/B/C" "/A/B/C/D/E" "C/D/E"
check "/A/B/C" "/A/B/D" "D"
check "/A/B/C" "/A/B/D/E" "D/E"
check "/A/B/C" "/A/D" "../D"
check "/A/B/C" "/A/D/E" "../D/E"
check "/A/B/C" "/D/E/F" "../../D/E/F"
check "/foo/baz/moo" "/foo/bar" "../bar"
check "http://example.com/foo/baz/moo" "http://example.com/foo/bar" "../bar"
check "http://example.com/foo/baz/moo/" "http://example.com/foo/bar" "../../bar"
check "http://example.com/foo/baz/moo" "http://example.com/foo/bar/" "../bar/"
check "http://example.com/foo/baz/moo/" "http://example.com/foo/bar/" "../../bar/"
এবং এখানে এই প্রশ্নটি থেকে পছন্দসই ফলাফল দেওয়ার জন্য কীভাবে ব্যবহার করা যেতে পারে:
absolute="/foo/bar"
current="/foo/baz/foo"
relative="$(relpathurl "$absolute" "$current/")"
echo "$relative"
যদি আপনি এমন কিছু খুঁজে পান যা কাজ করে না তবে দয়া করে নীচের মন্তব্যে আমাকে জানান। ধন্যবাদ।
পুনশ্চ:
কেন relpath
এখানে অন্য সমস্ত উত্তরগুলির বিপরীতে "বিপরীত" যুক্তি রয়েছে ?
আপনি যদি পরিবর্তন
Y="$(readlink -m -- "$2")" || return
প্রতি
Y="$(readlink -m -- "${2:-"$PWD"}")" || return
তারপরে আপনি ২ য় প্যারামিটারটি দূরে রাখতে পারেন, যেমন BASE বর্তমান ডিরেক্টরি / ইউআরএল / যা কিছু হোক। এটি যথারীতি কেবল ইউনিক্স নীতি।
যদি আপনি এটি অপছন্দ করেন তবে দয়া করে উইন্ডোজটিতে ফিরে যান। ধন্যবাদ।
দুঃখজনকভাবে, মার্ক রুশাকফের উত্তর (এখন মুছে ফেলা হয়েছে - এটি কোডটি এখানে থেকে রেফারেন্স করেছে ) মানিয়ে নেওয়ার সময় সঠিকভাবে কাজ করবে বলে মনে হয় না:
source=/home/part2/part3/part4
target=/work/proj1/proj2
বেশিরভাগ ক্ষেত্রে সঠিকভাবে কাজ করার জন্য ভাষ্যটিতে বর্ণিত চিন্তাভাবনা পরিমার্জন করা যেতে পারে। আমি ধরে নিতে চলেছি যে স্ক্রিপ্টটি একটি উত্স আর্গুমেন্ট (আপনি যেখানে আছেন) এবং একটি লক্ষ্য যুক্তি (যেখানে আপনি যেতে চান) নেন এবং উভয়ই পরম পথের নাম বা উভয়ই আপেক্ষিক। যদি একটি পরম এবং অন্যটি আপেক্ষিক হয় তবে সবচেয়ে সহজ কাজটি হ'ল বর্তমান ওয়ার্কিং ডিরেক্টরি সহ আপেক্ষিক নামটির উপসর্গ করা - তবে নীচের কোডটি এটি করে না।
নীচের কোডটি সঠিকভাবে কাজ করার কাছাকাছি, তবে একেবারেই সঠিক নয়।
xyz/./pqr
' এর মতো পথগুলিতে বিপথগামী 'বিন্দু' পরিচালনা করে না ।xyz/../pqr
' এর মতো পথগুলিতে বিপথগামী 'ডবল ডটস' পরিচালনা করে না ।./
পথ থেকে অগ্রণী ' ' সরিয়ে দেয় না ।ডেনিসের কোডটি আরও ভাল কারণ এটি 1 এবং 5 ঠিক করে - তবে একই সমস্যা 2, 3, 4 রয়েছে কারণ ডেনিসের কোডটি ব্যবহার করুন (এবং এর আগে এটি আপ করুন)
(এনবি: পসআইএক্স একটি সিস্টেম কল সরবরাহ করে realpath()
যা পাথের নামগুলি সমাধান করে যাতে সেগুলিতে কোনও চিহ্ন থাকে না the ইনপুট নামগুলিতে প্রয়োগ করা, এবং তারপরে ডেনিসের কোড ব্যবহার করে প্রতিবার সঠিক উত্তর দেওয়া যায় the সি কোডটি লিখলে এটি তুচ্ছ that মোড়ক realpath()
- আমি এটি সম্পন্ন করেছি - তবে আমি এমন কোনও স্ট্যান্ডার্ড ইউটিলিটি জানি না যা তা করে))
এর জন্য, আমি পার্লকে শেলের তুলনায় সহজেই ব্যবহার করা সহজ মনে করি, যদিও বাশের অ্যারেগুলির জন্য উপযুক্ত সমর্থন রয়েছে এবং সম্ভবত এটিও করতে পারে - পাঠকের জন্য অনুশীলন। সুতরাং, দুটি সুসংগত নাম দেওয়া, তাদের প্রতিটি উপাদান মধ্যে বিভক্ত:
এভাবে:
#!/bin/perl -w
use strict;
# Should fettle the arguments if one is absolute and one relative:
# Oops - missing functionality!
# Split!
my(@source) = split '/', $ARGV[0];
my(@target) = split '/', $ARGV[1];
my $count = scalar(@source);
$count = scalar(@target) if (scalar(@target) < $count);
my $relpath = "";
my $i;
for ($i = 0; $i < $count; $i++)
{
last if $source[$i] ne $target[$i];
}
$relpath = "." if ($i >= scalar(@source) && $relpath eq "");
for (my $s = $i; $s < scalar(@source); $s++)
{
$relpath = "../$relpath";
}
$relpath = "." if ($i >= scalar(@target) && $relpath eq "");
for (my $t = $i; $t < scalar(@target); $t++)
{
$relpath .= "/$target[$t]";
}
# Clean up result (remove double slash, trailing slash, trailing slash-dot).
$relpath =~ s%//%/%;
$relpath =~ s%/$%%;
$relpath =~ s%/\.$%%;
print "source = $ARGV[0]\n";
print "target = $ARGV[1]\n";
print "relpath = $relpath\n";
পরীক্ষার স্ক্রিপ্ট (বর্গাকার বন্ধনীতে একটি ফাঁকা এবং একটি ট্যাব থাকে):
sed 's/#.*//;/^[ ]*$/d' <<! |
/home/part1/part2 /home/part1/part3
/home/part1/part2 /home/part4/part5
/home/part1/part2 /work/part6/part7
/home/part1 /work/part1/part2/part3/part4
/home /work/part2/part3
/ /work/part2/part3/part4
/home/part1/part2 /home/part1/part2/part3/part4
/home/part1/part2 /home/part1/part2/part3
/home/part1/part2 /home/part1/part2
/home/part1/part2 /home/part1
/home/part1/part2 /home
/home/part1/part2 /
/home/part1/part2 /work
/home/part1/part2 /work/part1
/home/part1/part2 /work/part1/part2
/home/part1/part2 /work/part1/part2/part3
/home/part1/part2 /work/part1/part2/part3/part4
home/part1/part2 home/part1/part3
home/part1/part2 home/part4/part5
home/part1/part2 work/part6/part7
home/part1 work/part1/part2/part3/part4
home work/part2/part3
. work/part2/part3
home/part1/part2 home/part1/part2/part3/part4
home/part1/part2 home/part1/part2/part3
home/part1/part2 home/part1/part2
home/part1/part2 home/part1
home/part1/part2 home
home/part1/part2 .
home/part1/part2 work
home/part1/part2 work/part1
home/part1/part2 work/part1/part2
home/part1/part2 work/part1/part2/part3
home/part1/part2 work/part1/part2/part3/part4
!
while read source target
do
perl relpath.pl $source $target
echo
done
পরীক্ষার স্ক্রিপ্ট থেকে আউটপুট:
source = /home/part1/part2
target = /home/part1/part3
relpath = ../part3
source = /home/part1/part2
target = /home/part4/part5
relpath = ../../part4/part5
source = /home/part1/part2
target = /work/part6/part7
relpath = ../../../work/part6/part7
source = /home/part1
target = /work/part1/part2/part3/part4
relpath = ../../work/part1/part2/part3/part4
source = /home
target = /work/part2/part3
relpath = ../work/part2/part3
source = /
target = /work/part2/part3/part4
relpath = ./work/part2/part3/part4
source = /home/part1/part2
target = /home/part1/part2/part3/part4
relpath = ./part3/part4
source = /home/part1/part2
target = /home/part1/part2/part3
relpath = ./part3
source = /home/part1/part2
target = /home/part1/part2
relpath = .
source = /home/part1/part2
target = /home/part1
relpath = ..
source = /home/part1/part2
target = /home
relpath = ../..
source = /home/part1/part2
target = /
relpath = ../../../..
source = /home/part1/part2
target = /work
relpath = ../../../work
source = /home/part1/part2
target = /work/part1
relpath = ../../../work/part1
source = /home/part1/part2
target = /work/part1/part2
relpath = ../../../work/part1/part2
source = /home/part1/part2
target = /work/part1/part2/part3
relpath = ../../../work/part1/part2/part3
source = /home/part1/part2
target = /work/part1/part2/part3/part4
relpath = ../../../work/part1/part2/part3/part4
source = home/part1/part2
target = home/part1/part3
relpath = ../part3
source = home/part1/part2
target = home/part4/part5
relpath = ../../part4/part5
source = home/part1/part2
target = work/part6/part7
relpath = ../../../work/part6/part7
source = home/part1
target = work/part1/part2/part3/part4
relpath = ../../work/part1/part2/part3/part4
source = home
target = work/part2/part3
relpath = ../work/part2/part3
source = .
target = work/part2/part3
relpath = ../work/part2/part3
source = home/part1/part2
target = home/part1/part2/part3/part4
relpath = ./part3/part4
source = home/part1/part2
target = home/part1/part2/part3
relpath = ./part3
source = home/part1/part2
target = home/part1/part2
relpath = .
source = home/part1/part2
target = home/part1
relpath = ..
source = home/part1/part2
target = home
relpath = ../..
source = home/part1/part2
target = .
relpath = ../../..
source = home/part1/part2
target = work
relpath = ../../../work
source = home/part1/part2
target = work/part1
relpath = ../../../work/part1
source = home/part1/part2
target = work/part1/part2
relpath = ../../../work/part1/part2
source = home/part1/part2
target = work/part1/part2/part3
relpath = ../../../work/part1/part2/part3
source = home/part1/part2
target = work/part1/part2/part3/part4
relpath = ../../../work/part1/part2/part3/part4
এই পার্ল স্ক্রিপ্টটি অদ্ভুত ইনপুটগুলির মুখে ইউনিক্সে (এটি উইন্ডোজ পাথের নামগুলির সমস্ত জটিলতা বিবেচনায় নেয় না) মোটামুটিভাবে পুরোপুরি কাজ করে। এটি বিদ্যমান যে নামগুলির আসল পথটি সমাধান করতে মডিউল Cwd
এবং এর কার্যকারিতাটি ব্যবহার করে এবং realpath
অস্তিত্বহীন পাথগুলির জন্য একটি পাঠ্য বিশ্লেষণ করে। একটি ব্যতীত অন্যান্য ক্ষেত্রে এটি ডেনিসের স্ক্রিপ্টের মতো একই আউটপুট উত্পাদন করে। বিচ্যুত কেসটি হ'ল:
source = home/part1/part2
target = .
relpath1 = ../../..
relpath2 = ../../../.
দুটি ফলাফল সমান - কেবল অভিন্ন নয়। (আউটপুটটি পরীক্ষা স্ক্রিপ্টের একটি হালকা পরিবর্তিত সংস্করণ থেকে এসেছে - উপরের স্ক্রিপ্টের মতো ইনপুট এবং উত্তরের চেয়ে নীচের পার্ল স্ক্রিপ্টটি কেবল উত্তর মুদ্রণ করে)) এখন: আমি কি কর্মহীন উত্তরটি মুছে ফেলা উচিত? হতে পারে...
#!/bin/perl -w
# Based loosely on code from: http://unix.derkeiler.com/Newsgroups/comp.unix.shell/2005-10/1256.html
# Via: http://stackoverflow.com/questions/2564634
use strict;
die "Usage: $0 from to\n" if scalar @ARGV != 2;
use Cwd qw(realpath getcwd);
my $pwd;
my $verbose = 0;
# Fettle filename so it is absolute.
# Deals with '//', '/./' and '/../' notations, plus symlinks.
# The realpath() function does the hard work if the path exists.
# For non-existent paths, the code does a purely textual hack.
sub resolve
{
my($name) = @_;
my($path) = realpath($name);
if (!defined $path)
{
# Path does not exist - do the best we can with lexical analysis
# Assume Unix - not dealing with Windows.
$path = $name;
if ($name !~ m%^/%)
{
$pwd = getcwd if !defined $pwd;
$path = "$pwd/$path";
}
$path =~ s%//+%/%g; # Not UNC paths.
$path =~ s%/$%%; # No trailing /
$path =~ s%/\./%/%g; # No embedded /./
# Try to eliminate /../abc/
$path =~ s%/\.\./(?:[^/]+)(/|$)%$1%g;
$path =~ s%/\.$%%; # No trailing /.
$path =~ s%^\./%%; # No leading ./
# What happens with . and / as inputs?
}
return($path);
}
sub print_result
{
my($source, $target, $relpath) = @_;
if ($verbose)
{
print "source = $ARGV[0]\n";
print "target = $ARGV[1]\n";
print "relpath = $relpath\n";
}
else
{
print "$relpath\n";
}
exit 0;
}
my($source) = resolve($ARGV[0]);
my($target) = resolve($ARGV[1]);
print_result($source, $target, ".") if ($source eq $target);
# Split!
my(@source) = split '/', $source;
my(@target) = split '/', $target;
my $count = scalar(@source);
$count = scalar(@target) if (scalar(@target) < $count);
my $relpath = "";
my $i;
# Both paths are absolute; Perl splits an empty field 0.
for ($i = 1; $i < $count; $i++)
{
last if $source[$i] ne $target[$i];
}
for (my $s = $i; $s < scalar(@source); $s++)
{
$relpath = "$relpath/" if ($s > $i);
$relpath = "$relpath..";
}
for (my $t = $i; $t < scalar(@target); $t++)
{
$relpath = "$relpath/" if ($relpath ne "");
$relpath = "$relpath$target[$t]";
}
print_result($source, $target, $relpath);
/home/part1/part2
কাছে /
এক অনেকগুলি হয়েছে ../
। অন্যথায়, আমার স্ক্রিপ্টটি আমার ব্যতীত আপনার আউটপুটটির সাথে মিলে যায় .
যেখানে গন্তব্যস্থলটির শেষে একটি অপ্রয়োজনীয় যোগ করে .
এবং আমি উপরে না ./
গিয়ে নামার শুরুতে একটি ব্যবহার করি না ।
readlink /usr/bin/vi
দেয় /etc/alternatives/vi
, তবে এটি অন্য একটি সিমলিংক - যেখানে readlink -f /usr/bin/vi
দেয় /usr/bin/vim.basic
যা সমস্ত
আমি আপনার প্রশ্নটিকে "পোর্টেবল" শেল কোডে এটি লেখার চ্যালেঞ্জ হিসাবে গ্রহণ করেছি, অর্থাৎ
এটি কোনও পসিক্স কনফর্মেন্ট শেল (zsh, bash, ksh, অ্যাশ, ব্যস্তবক্স, ...) এ চলে। এমনকি এটির ক্রিয়াকলাপ যাচাই করার জন্য এটিতে একটি টেস্টসাইট রয়েছে contains পথের নামগুলির ক্যানোনিকালাইজেশনটি অনুশীলন হিসাবে ছেড়ে গেছে। :-)
#!/bin/sh
# Find common parent directory path for a pair of paths.
# Call with two pathnames as args, e.g.
# commondirpart foo/bar foo/baz/bat -> result="foo/"
# The result is either empty or ends with "/".
commondirpart () {
result=""
while test ${#1} -gt 0 -a ${#2} -gt 0; do
if test "${1%${1#?}}" != "${2%${2#?}}"; then # First characters the same?
break # No, we're done comparing.
fi
result="$result${1%${1#?}}" # Yes, append to result.
set -- "${1#?}" "${2#?}" # Chop first char off both strings.
done
case "$result" in
(""|*/) ;;
(*) result="${result%/*}/";;
esac
}
# Turn foo/bar/baz into ../../..
#
dir2dotdot () {
OLDIFS="$IFS" IFS="/" result=""
for dir in $1; do
result="$result../"
done
result="${result%/}"
IFS="$OLDIFS"
}
# Call with FROM TO args.
relativepath () {
case "$1" in
(*//*|*/./*|*/../*|*?/|*/.|*/..)
printf '%s\n' "'$1' not canonical"; exit 1;;
(/*)
from="${1#?}";;
(*)
printf '%s\n' "'$1' not absolute"; exit 1;;
esac
case "$2" in
(*//*|*/./*|*/../*|*?/|*/.|*/..)
printf '%s\n' "'$2' not canonical"; exit 1;;
(/*)
to="${2#?}";;
(*)
printf '%s\n' "'$2' not absolute"; exit 1;;
esac
case "$to" in
("$from") # Identical directories.
result=".";;
("$from"/*) # From /x to /x/foo/bar -> foo/bar
result="${to##$from/}";;
("") # From /foo/bar to / -> ../..
dir2dotdot "$from";;
(*)
case "$from" in
("$to"/*) # From /x/foo/bar to /x -> ../..
dir2dotdot "${from##$to/}";;
(*) # Everything else.
commondirpart "$from" "$to"
common="$result"
dir2dotdot "${from#$common}"
result="$result/${to#$common}"
esac
;;
esac
}
set -f # noglob
set -x
cat <<EOF |
/ / .
/- /- .
/? /? .
/?? /?? .
/??? /??? .
/?* /?* .
/* /* .
/* /** ../**
/* /*** ../***
/*.* /*.** ../*.**
/*.??? /*.?? ../*.??
/[] /[] .
/[a-z]* /[0-9]* ../[0-9]*
/foo /foo .
/foo / ..
/foo/bar / ../..
/foo/bar /foo ..
/foo/bar /foo/baz ../baz
/foo/bar /bar/foo ../../bar/foo
/foo/bar/baz /gnarf/blurfl/blubb ../../../gnarf/blurfl/blubb
/foo/bar/baz /gnarf ../../../gnarf
/foo/bar/baz /foo/baz ../../baz
/foo. /bar. ../bar.
EOF
while read FROM TO VIA; do
relativepath "$FROM" "$TO"
printf '%s\n' "FROM: $FROM" "TO: $TO" "VIA: $result"
if test "$result" != "$VIA"; then
printf '%s\n' "OOOPS! Expected '$VIA' but got '$result'"
fi
done
# vi: set tabstop=3 shiftwidth=3 expandtab fileformat=unix :
আমার সমাধান:
computeRelativePath()
{
Source=$(readlink -f ${1})
Target=$(readlink -f ${2})
local OLDIFS=$IFS
IFS="/"
local SourceDirectoryArray=($Source)
local TargetDirectoryArray=($Target)
local SourceArrayLength=$(echo ${SourceDirectoryArray[@]} | wc -w)
local TargetArrayLength=$(echo ${TargetDirectoryArray[@]} | wc -w)
local Length
test $SourceArrayLength -gt $TargetArrayLength && Length=$SourceArrayLength || Length=$TargetArrayLength
local Result=""
local AppendToEnd=""
IFS=$OLDIFS
local i
for ((i = 0; i <= $Length + 1 ; i++ ))
do
if [ "${SourceDirectoryArray[$i]}" = "${TargetDirectoryArray[$i]}" ]
then
continue
elif [ "${SourceDirectoryArray[$i]}" != "" ] && [ "${TargetDirectoryArray[$i]}" != "" ]
then
AppendToEnd="${AppendToEnd}${TargetDirectoryArray[${i}]}/"
Result="${Result}../"
elif [ "${SourceDirectoryArray[$i]}" = "" ]
then
Result="${Result}${TargetDirectoryArray[${i}]}/"
else
Result="${Result}../"
fi
done
Result="${Result}${AppendToEnd}"
echo $Result
}
এখানে আমার সংস্করণ। এটা তোলে উপর ভিত্তি করে এর উত্তর দ্বারা @Offirmo । আমি এটিকে ড্যাশ-সামঞ্জস্যপূর্ণ করে তুলেছি এবং নিম্নলিখিত টেস্টকেস ব্যর্থতাটিকে ঠিক করেছি:
./compute-relative.sh "/a/b/c/de/f/g" "/a/b/c/def/g/"
-> "../..f/g/"
এখন:
CT_FindRelativePath "/a/b/c/de/f/g" "/a/b/c/def/g/"
-> "../../../def/g/"
কোডটি দেখুন:
# both $1 and $2 are absolute paths beginning with /
# returns relative path to $2/$target from $1/$source
CT_FindRelativePath()
{
local insource=$1
local intarget=$2
# Ensure both source and target end with /
# This simplifies the inner loop.
#echo "insource : \"$insource\""
#echo "intarget : \"$intarget\""
case "$insource" in
*/) ;;
*) source="$insource"/ ;;
esac
case "$intarget" in
*/) ;;
*) target="$intarget"/ ;;
esac
#echo "source : \"$source\""
#echo "target : \"$target\""
local common_part=$source # for now
local result=""
#echo "common_part is now : \"$common_part\""
#echo "result is now : \"$result\""
#echo "target#common_part : \"${target#$common_part}\""
while [ "${target#$common_part}" = "${target}" -a "${common_part}" != "//" ]; 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
if [ -z "${result}" ]; then
result="../"
else
result="../$result"
fi
#echo "(w) common_part is now : \"$common_part\""
#echo "(w) result is now : \"$result\""
#echo "(w) target#common_part : \"${target#$common_part}\""
done
#echo "(f) common_part is : \"$common_part\""
if [ "${common_part}" = "//" ]; then
# special case for root (no common path)
common_part="/"
fi
# since we now have identified the common part,
# compute the non-common part
forward_part="${target#$common_part}"
#echo "forward_part = \"$forward_part\""
if [ -n "${result}" -a -n "${forward_part}" ]; then
#echo "(simple concat)"
result="$result$forward_part"
elif [ -n "${forward_part}" ]; then
result="$forward_part"
fi
#echo "result = \"$result\""
# if a / was added to target and result ends in / then remove it now.
if [ "$intarget" != "$target" ]; then
case "$result" in
*/) result=$(echo "$result" | awk '{ string=substr($0, 1, length($0)-1); print string; }' ) ;;
esac
fi
echo $result
return 0
}
অনুমান করুন যে এটি কৌশলটিও করবে ... (অন্তর্নির্মিত পরীক্ষার সাথে আসে) :)
ঠিক আছে, কিছু ওভারহেড প্রত্যাশিত, কিন্তু আমরা এখানে বোর্ন শেল করছি! ;)
#!/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
এই স্ক্রিপ্টটি কেবল পথের নামগুলিতে কাজ করে। এটি বিদ্যমান ফাইলের কোনও প্রয়োজন হয় না। পাস করা পথগুলি যদি নিখুঁত না হয় তবে আচরণটি কিছুটা অস্বাভাবিক, তবে উভয় পথই যদি আপেক্ষিক হয় তবে এটি প্রত্যাশা অনুযায়ী কাজ করা উচিত।
আমি এটি কেবল ওএস এক্সে পরীক্ষা করেছি, সুতরাং এটি পোর্টেবল নাও হতে পারে।
#!/bin/bash
set -e
declare SCRIPT_NAME="$(basename $0)"
function usage {
echo "Usage: $SCRIPT_NAME <base path> <target file>"
echo " Outputs <target file> relative to <base path>"
exit 1
}
if [ $# -lt 2 ]; then usage; fi
declare base=$1
declare target=$2
declare -a base_part=()
declare -a target_part=()
#Split path elements & canonicalize
OFS="$IFS"; IFS='/'
bpl=0;
for bp in $base; do
case "$bp" in
".");;
"..") let "bpl=$bpl-1" ;;
*) base_part[${bpl}]="$bp" ; let "bpl=$bpl+1";;
esac
done
tpl=0;
for tp in $target; do
case "$tp" in
".");;
"..") let "tpl=$tpl-1" ;;
*) target_part[${tpl}]="$tp" ; let "tpl=$tpl+1";;
esac
done
IFS="$OFS"
#Count common prefix
common=0
for (( i=0 ; i<$bpl ; i++ )); do
if [ "${base_part[$i]}" = "${target_part[$common]}" ] ; then
let "common=$common+1"
else
break
fi
done
#Compute number of directories up
let "updir=$bpl-$common" || updir=0 #if the expression is zero, 'let' fails
#trivial case (after canonical decomposition)
if [ $updir -eq 0 ]; then
echo .
exit
fi
#Print updirs
for (( i=0 ; i<$updir ; i++ )); do
echo -n ../
done
#Print remaining path
for (( i=$common ; i<$tpl ; i++ )); do
if [ $i -ne $common ]; then
echo -n "/"
fi
if [ "" != "${target_part[$i]}" ] ; then
echo -n "${target_part[$i]}"
fi
done
#One last newline
echo
এই উত্তরটি প্রশ্নের বাশ অংশটিকে সম্বোধন করে না, তবে ইমাসে এই কার্যকারিতাটি প্রয়োগ করতে আমি এই প্রশ্নের উত্তরগুলি ব্যবহার করার চেষ্টা করেছি বলে আমি এটি এখান থেকে ফেলে দেব।
ইমাকস আসলে বাক্সের বাইরে এটির জন্য একটি ফাংশন রয়েছে:
ELISP> (file-relative-name "/a/b/c" "/a/b/c")
"."
ELISP> (file-relative-name "/a/b/c" "/a/b")
"c"
ELISP> (file-relative-name "/a/b/c" "/c/b")
"../../a/b/c"
relpath
ফাংশন) আপনি সরবরাহ file-relative-name
করেছেন এমন পরীক্ষাগুলির ক্ষেত্রে অভিন্ন আচরণ করে ।
এখানে একটি শেল স্ক্রিপ্ট রয়েছে যা এটি অন্যান্য প্রোগ্রামগুলি কল না করে এটি করে:
#! /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
আমার এ জাতীয় কিছু দরকার ছিল যা প্রতীকী লিঙ্কগুলিও সমাধান করেছিল। আমি আবিষ্কার করেছি যে পিডব্লুডির সেই উদ্দেশ্যে একটি -P পতাকা রয়েছে। আমার স্ক্রিপ্টের একটি অংশ যুক্ত হয়েছে। এটি শেল স্ক্রিপ্টের একটি ফাংশনের মধ্যে, সুতরাং $ 1 এবং $ 2। ফলাফল মান, যা START_ABS থেকে END_ABS পর্যন্ত আপেক্ষিক পথ, UPDIRS ভেরিয়েবলে রয়েছে। স্ক্রিপ্ট সিডি প্রতিটি প্যারামিটার ডিরেক্টরিতে পিডব্লুডি-পি চালানোর জন্য এবং এর অর্থ এই যে আপেক্ষিক পথের পরামিতিগুলি পরিচালনা করা হয়। চিয়ার্স, জিম
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"