รับลิงก์ที่สัมพันธ์กันระหว่างสองพา ธ


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>ใช้ญาติเชื่อมโยงสัญลักษณ์เมื่อใดก็ตามที่เป็นไปได้ตั้งแต่มิฉะนั้นเซิร์ฟเวอร์แซมบ้าของเราไม่ได้แสดงไฟล์ได้อย่างถูกต้องเมื่อฉันเข้าถึงไฟล์เหล่านี้บนเครือข่ายจาก Windows (ที่ผมไม่ได้เป็นผู้ดูแลระบบ SYS และ Don' ไม่มีการควบคุมการตั้งค่านี้)

สมมติว่า<target_path>และ<source_path>เป็นเส้นทางที่แน่นอนต่อไปนี้จะสร้างการเชื่อมโยงสัญลักษณ์ที่ชี้ไปยังเส้นทางที่แน่นอน

ln -s <target_path> <source_path>

ดังนั้นมันจึงไม่ทำงานสำหรับความต้องการของฉัน ฉันต้องทำสิ่งนี้กับไฟล์หลายร้อยไฟล์ดังนั้นฉันไม่สามารถแก้ไขด้วยตนเองได้

บิวด์อินใด ๆ ที่ดูแลสิ่งนี้?


คุณสามารถใช้symlinksในการแปลงลิงค์สัมบูรณ์เป็นญาติ
Stéphane Chazelas

@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) ที่ใช้สองพา ธ (ต้นทางและปลายทาง) และส่งเอาต์พุตพา ธ สัมพัทธ์จากต้นทางไปยังเป้าหมาย (ไม่มีลิงก์สัญลักษณ์ที่นี่) นั่นจะแก้คำถามที่ถามในหัวข้อด้วย
Amelio Vazquez-Reina


24

ลองใช้realpathคำสั่ง (ส่วนหนึ่งของ GNU coreutils; > = 8.23 ) เช่น:

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

หากคุณกำลังใช้ MacOS ติดตั้ง GNU รุ่นผ่าน: และการใช้งานbrew install coreutilsgrealpath

โปรดทราบว่าทั้งสองพา ธ ต้องมีอยู่เพื่อให้คำสั่งประสบความสำเร็จ หากคุณต้องการเส้นทางสัมพัทธ์ต่อไปแม้ว่าหนึ่งในนั้นจะไม่มีอยู่ให้เพิ่มสวิตช์ -m

สำหรับตัวอย่างเพิ่มเติมโปรดดูที่แปลงเส้นทางแน่นอนเข้ามาในเส้นทางญาติรับไดเรกทอรีปัจจุบัน


ฉันได้รับrealpath: unrecognized option '--relative-to=.':( รุ่นที่ใช้งานจริง 1.19
phuclv

@ LưuVĩnhPhúcคุณจำเป็นที่จะต้องใช้รุ่น GNU / Linux realpathของ หากคุณอยู่ใน MacOS brew install coreutilsติดตั้งผ่าน:
kenorb

ไม่ฉันใช้ Ubuntu 14.04 LTS
phuclv

@ LưuVĩnhPhúcฉันกำลังrealpath (GNU coreutils) 8.26ใช้พารามิเตอร์ที่อยู่ ดู: gnu.org/software/coreutils/realpathตรวจสอบว่าคุณไม่ได้ใช้นามแฝง (เช่นการทำงานเป็น\realpath) coreutilsและวิธีที่คุณสามารถติดตั้งรุ่นจากที่
kenorb


7

ฉันคิดว่าโซลูชัน python นี้ (นำมาจากการโพสต์เดียวกับคำตอบของ @ 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 นี่คือการคัดลอกโดยตรงจากคำตอบที่เชื่อมโยง ผมเป็นคน Perl sys.argvและรู้หลักไม่หลามดังนั้นผมจึงไม่ทราบวิธีการใช้งาน หากรหัสที่โพสต์ผิดหรือสามารถปรับปรุงได้โปรดขอบคุณ
terdon

@StephaneChazelas ฉันเข้าใจแล้วขอขอบคุณสำหรับการแก้ไข
terdon

7

ไม่มีตัวสร้างเชลล์ที่จะดูแลสิ่งนี้ ฉันพบวิธีแก้ไขปัญหา Stack Overflowแล้ว ฉันคัดลอกโซลูชันที่นี่พร้อมการแก้ไขเล็กน้อยและทำเครื่องหมายคำตอบนี้เป็นวิกิชุมชน

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/}"
}

นี่เป็นวิธีแก้ปัญหาที่ง่ายและสวยงามที่สุด

นอกจากนี้ควรใช้กับกระสุนที่มีลักษณะคล้าย bourne ทั้งหมด

และภูมิปัญญาที่นี่คือ: ไม่จำเป็นต้องมีสาธารณูปโภคภายนอกอย่างแน่นอนหรือแม้แต่สภาพที่ซับซ้อน การทดแทนคำนำหน้า / คำต่อท้ายคู่และห่วงในขณะที่เป็นสิ่งที่คุณต้องทำทุกอย่างที่ทำบนเปลือกหอยเหมือน bourne

นอกจากนี้ยังมีให้บริการผ่าน PasteBin: http://pastebin.com/CtTfvime


ขอบคุณสำหรับการแก้ปัญหาของคุณ ฉันพบว่าการทำให้มันทำงานใน a 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เป็นฟังก์ชั่นทุบตีโดยคำสั่งดังต่อไปนี้ (ต้องการ bash 4+)

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

หรือเรียกใช้สคริปต์แบบทันทีทันใดเหมือนกับ curl bash combo

curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- /a/b/c/d/e /a/b/CC/DD/EE
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.