दो रास्तों के बीच सापेक्ष संबंध बनाना


21

कहो मेरे पास दो रास्ते हैं: <source_path>और <target_path>। मैं अपने खोल (zsh) की तरह स्वचालित रूप से पता लगाने के लिए अगर वहाँ का प्रतिनिधित्व करने के लिए एक रास्ता है होगा <target_path>से <source_path>एक रिश्तेदार पथ के रूप में।

जैसे मान लेते हैं

  • <source_path> है /foo/bar/something
  • <target_path> है /foo/hello/world

परिणाम होगा ../../hello/world

मुझे इसकी आवश्यकता क्यों है:

मुझे जब भी संभव हो, किसी रिश्तेदार प्रतीकात्मक लिंक का उपयोग <source_path>करने से एक प्रतीकात्मक लिंक बनाने की आवश्यकता है, अन्यथा जब तक हम विंडोज़ से नेटवर्क पर इन फ़ाइलों तक नहीं पहुंचते हैं, तब तक मैं हमारे सांबा सर्वर को फ़ाइल को ठीक से नहीं दिखाता हूं (मैं एसवाईएस व्यवस्थापक नहीं हूं, और डॉन ' टी इस सेटिंग पर नियंत्रण है)<target_path>

यह मानते हुए कि <target_path>और <source_path>पूर्ण पथ हैं, निम्नलिखित एक निरपेक्ष पथ की ओर इशारा करते हुए एक प्रतीकात्मक लिंक बनाता है ।

ln -s <target_path> <source_path>

इसलिए यह मेरी जरूरतों के लिए काम नहीं करता है। मुझे सैकड़ों फ़ाइलों के लिए ऐसा करने की आवश्यकता है, इसलिए मैं इसे मैन्युअल रूप से ठीक नहीं कर सकता।

किसी भी शेल बिल्ट-इन का ख्याल रखें?


आप symlinksसापेक्ष के लिए पूर्ण लिंक परिवर्तित करने के लिए उपयोग कर सकते हैं ।
स्टीफन चेज़लस

@StephaneChazelas कैसे? क्या आप एक उत्तर के बारे में बता सकते हैं?
terdon

जवाबों:


10

आप symlinksसापेक्ष के लिए पूर्ण पथ बदलने के लिए कमांड का उपयोग कर सकते हैं :

/tmp$ mkdir -p 1/{a,b,c} 2
/tmp$ cd 2
/tmp/2$ ln -s /tmp/1/* .
/tmp/2$ ls -l
total 0
lrwxrwxrwx 1 stephane stephane 8 Jul 31 16:32 a -> /tmp/1/a/
lrwxrwxrwx 1 stephane stephane 8 Jul 31 16:32 b -> /tmp/1/b/
lrwxrwxrwx 1 stephane stephane 8 Jul 31 16:32 c -> /tmp/1/c/

हमें पूर्ण लिंक मिल गए हैं, आइए उन्हें सापेक्ष में परिवर्तित करें:

/tmp/2$ symlinks -cr .
absolute: /tmp/2/a -> /tmp/1/a
changed:  /tmp/2/a -> ../1/a
absolute: /tmp/2/b -> /tmp/1/b
changed:  /tmp/2/b -> ../1/b
absolute: /tmp/2/c -> /tmp/1/c
changed:  /tmp/2/c -> ../1/c
/tmp/2$ ls -l
total 0
lrwxrwxrwx 1 stephane stephane 6 Jul 31 16:32 a -> ../1/a/
lrwxrwxrwx 1 stephane stephane 6 Jul 31 16:32 b -> ../1/b/
lrwxrwxrwx 1 stephane stephane 6 Jul 31 16:32 c -> ../1/c/

संदर्भ


धन्यवाद स्टीफन। यह मूल समस्या को हल करता है, इसलिए मैंने स्वीकार किया है। उस ने कहा, कुछ होना बहुत अच्छा होगा (जैसे एक zsh फ़ंक्शन) जो दो रास्तों (स्रोत और लक्ष्य) को लेता है और स्रोत से सापेक्ष पथ को लक्षित करता है (यहाँ कोई प्रतीकात्मक लिंक नहीं है)। यह शीर्षक में पूछे गए प्रश्न को भी हल करेगा।
एमिलियो वाज़केज़-रीना


24

realpathकमांड (GNU का हिस्सा coreutils; > = 8.23 ) का उपयोग करके देखें , जैसे:

realpath --relative-to=/foo/bar/something /foo/hello/world

यदि आप macOS का उपयोग कर रहे हैं, के माध्यम से GNU संस्करण स्थापित brew install coreutilsकरें : और उपयोग करें grealpath

ध्यान दें कि सफल होने के लिए कमांड के लिए दोनों रास्तों का होना आवश्यक है। यदि आपको वैसे भी सापेक्ष पथ की आवश्यकता है, भले ही उनमें से कोई मौजूद न हो तो -m स्विच जोड़ें।

अधिक उदाहरणों के लिए, निरपेक्ष पथ को वर्तमान निर्देशिका में दिए गए सापेक्ष पथ में परिवर्तित करें देखें ।


मुझे मिलता है realpath: unrecognized option '--relative-to=.':( realpath संस्करण 1.19
phuclv

@ LưuV LnhPhúc आपको GNU / Linux संस्करण का उपयोग करने की आवश्यकता है realpath। यदि आप macOS पर हैं, तो इसके जरिए इंस्टॉल करें brew install coreutils:।
kenorb

नहीं, मैं उबंटू 14.04 एलटीएस पर हूँ
phuclv

@ LưuV LnhPhúc मैं वहां हूं realpath (GNU coreutils) 8.26जहां पैरामीटर मौजूद है। देखें: gnu.org/software/coreutils/realpath डबल जांचें कि आप एक उपनाम (जैसे रन नहीं) का उपयोग नहीं कर रहे हैं \realpath, और आप संस्करण को कैसे स्थापित कर सकते हैं coreutils
kenorb


7

मुझे लगता है कि इस अजगर समाधान (@ EvanTeitelman के जवाब के रूप में एक ही पोस्ट से लिया गया) भी ध्यान देने योग्य है। इसे अपने में जोड़ें ~/.zshrc:

relpath() python -c 'import os.path, sys;\
  print os.path.relpath(sys.argv[1],sys.argv[2])' "$1" "${2-$PWD}"

आप तब कर सकते हैं, उदाहरण के लिए:

$ relpath /usr/local/share/doc/emacs /usr/local/share/fonts
../doc/emacs

@StephaneChazelas यह जुड़े हुए उत्तर से एक सीधा कॉपी पेस्ट था। मैं एक पर्ल लड़का हूं और अनिवार्य रूप से कोई अजगर नहीं जानता, इसलिए मुझे नहीं पता कि कैसे उपयोग करना है sys.argv। यदि पोस्ट किया गया कोड गलत है या सुधार किया जा सकता है, तो मेरे धन्यवाद के साथ ऐसा करने के लिए स्वतंत्र महसूस करें।
terdon

@StephaneChazelas मुझे अब समझ में आया, संपादन के लिए धन्यवाद।
terdon

7

इसकी देखभाल के लिए कोई शेल बिल्डिंग्स नहीं हैं। मैंने हालांकि स्टैक ओवरफ्लो पर एक समाधान पाया । मैंने हल को मामूली संशोधनों के साथ कॉपी किया है और इस उत्तर को समुदाय विकि के रूप में चिह्नित किया है।

relpath() {
    # both $1 and $2 are absolute paths beginning with /
    # $1 must be a canonical path; that is none of its directory
    # components may be ".", ".." or a symbolic link
    #
    # returns relative path to $2/$target from $1/$source
    source=$1
    target=$2

    common_part=$source
    result=

    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#?}
    fi

    printf '%s\n' "$result"
}

आप इस फ़ंक्शन का उपयोग कर सकते हैं:

source=/foo/bar/something
target=/foo/hello/world
ln -s "$(relpath "$source" "$target")" "$source"

3
# Prints out the relative path between to absolute paths. Trivial.
#
# Parameters:
# $1 = first path
# $2 = second path
#
# Output: the relative path between 1st and 2nd paths
relpath() {
    local pos="${1%%/}" ref="${2%%/}" down=''

    while :; do
        test "$pos" = '/' && break
        case "$ref" in $pos/*) break;; esac
        down="../$down"
        pos=${pos%/*}
    done

    echo "$down${ref##$pos/}"
}

यह समस्या का सबसे सरल और सबसे सुंदर समाधान है।

इसके अलावा यह सभी बोर्न-जैसे गोले पर काम करना चाहिए।

और यहां ज्ञान यह है: बाहरी उपयोगिताओं या यहां तक ​​कि जटिल परिस्थितियों के लिए बिल्कुल कोई आवश्यकता नहीं है। एक युगल उपसर्ग / प्रत्यय प्रतिस्थापन और थोड़ी देर के लूप आप सभी को बोर्न-जैसे गोले पर बस कुछ भी प्राप्त करने की आवश्यकता है।

PasteBin के माध्यम से भी उपलब्ध है: http://pastebin.com/CtTfvime


आपके समाधान के लिए धन्यवाद। मैंने पाया कि Makefileमुझे एक पंक्ति में दोहरे उद्धरण चिह्नों को जोड़ने की आवश्यकता थी pos=${pos%/*}(और, निश्चित रूप से, अर्धविराम और बैकस्लैश हर पंक्ति के अंत में) जोड़ने की आवश्यकता है।
Circonflexe

आइडिया बुरा नहीं है, लेकिन, मामलों के वर्तमान संस्करण प्रचुर मात्रा में काम नहीं करते: relpath /tmp /tmp, relpath /tmp /tmp/a, relpath /tmp/a /। जोड़ने में, आप यह उल्लेख करना भूल जाते हैं कि सभी रास्ते पूर्ण और विहित होने चाहिए (यानी /tmp/a/..काम नहीं करते)
Jérôme Pouiller

0

मुझे इसे मज़बूती से करने के लिए एक बैश स्क्रिप्ट लिखनी थी (और निश्चित रूप से फ़ाइल अस्तित्व की पुष्टि किए बिना)।

प्रयोग

relpath <symlink path> <source path>

एक सापेक्ष स्रोत पथ लौटाता है।

उदाहरण:

#/a/b/c/d/e   ->  /a/b/CC/DD/EE       ->  ../../CC/DD/EE
relpath /a/b/c/d/e   /a/b/CC/DD/EE

#./a/b/c/d/e   ->  ./a/b/CC/DD/EE       ->  ../../CC/DD/EE
relpath ./a/b/c/d/e  ./a/b/CC/DD/EE

दोनों मिलते हैं ../../CC/DD/EE


त्वरित परीक्षण के लिए आप दौड़ सकते हैं

curl -sL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- --test

परिणाम: परीक्षण की एक सूची

/a             ->  /                 ->  .
/a/b           ->  /                 ->  ..
/a/b           ->  /a                ->  .
/a/b/c         ->  /a                ->  ..
/a/b/c         ->  /a/b              ->  .
/a/b/c         ->  /a/b/CC           ->  CC
/a/b/c/d/e     ->  /a                ->  ../../..
/a/b/c/d/e     ->  /a/b              ->  ../..
/a/b/c/d/e     ->  /a/b/CC           ->  ../../CC
/a/b/c/d/e     ->  /a/b/CC/DD/EE     ->  ../../CC/DD/EE
./a            ->  ./                ->  .
./a/b          ->  ./                ->  ..
./a/b          ->  ./a               ->  .
./a/b/c        ->  ./a               ->  ..
./a/b/c        ->  ./a/b             ->  .
./a/b/c        ->  ./a/b/CC          ->  CC
./a/b/c/d/e    ->  ./a               ->  ../../..
./a/b/c/d/e    ->  ./a/b/CC          ->  ../../CC
./a/b/c/d/e    ->  ./a/b/CC/DD/EE    ->  ../../CC/DD/EE
/a             ->  /x                ->  x
/a/b           ->  /x                ->  ../x
/a/b/c         ->  /x                ->  ../../x
/a             ->  /x/y              ->  x/y
/a/b           ->  /x/y              ->  ../x/y
/a/b/c         ->  /x/y              ->  ../../x/y
/x             ->  /a                ->  a
/x             ->  /a/b              ->  a/b
/x             ->  /a/b/c            ->  a/b/c
/x/y           ->  /a                ->  ../a
/x/y           ->  /a/b              ->  ../a/b
/x/y           ->  /a/b/c            ->  ../a/b/c
./a a/b/c/d/e  ->  ./a a/b/CC/DD/EE  ->  ../../CC/DD/EE
/x x/y y       ->  /a a/b b/c c      ->  ../a a/b b/c c

BTW, अगर आप इस स्क्रिप्ट को बिना सहेजे उपयोग करना चाहते हैं और आप इस स्क्रिप्ट पर भरोसा करते हैं, तो आपके पास इसे बैश ट्रिक के दो तरीके हैं।

relpathनिम्न आदेश द्वारा बैश फ़ंक्शन के रूप में आयात करें । (बैश 4+ की आवश्यकता है)

source <(curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath)

या स्क्रिप्ट को कर्ल बैश कॉम्बो की तरह ऑन-द-फ्लाई कहते हैं।

curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- /a/b/c/d/e /a/b/CC/DD/EE
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.