İki yol arasındaki bağıl bağlantıların alınması


21

İki yolum olduğunu söyle: <source_path>ve <target_path>. Ben temsil edecek bir yol olup olmadığını otomatik olarak öğrenmek için benim kabuk (zsh) istiyorum <target_path>den <source_path>göreceli yol olarak.

Örneğin, farz edelim

  • <source_path> olduğu /foo/bar/something
  • <target_path> olduğu /foo/hello/world

Sonuç olacaktır ../../hello/world

Neden buna ihtiyacım var:

Ben sembolik bir bağlantı oluşturmak ister gerek <source_path>için <target_path>bir kullanarak göreceli 'ı sys yönetici değilim (ben Windows'tan ağ üzerindeki bu dosyaları erişirken aksi bizim samba sunucu dosyası düzgün görünmüyor, çünkü simgesel bağlantı mümkün olduğunda ve don Bu ayar üzerinde kontrolü yok)

Bunun <target_path>ve <source_path>mutlak yollar olduğunu varsayarak , aşağıdakiler mutlak bir yola işaret eden sembolik bir bağ oluşturur .

ln -s <target_path> <source_path>

bu yüzden ihtiyaçlarım için işe yaramaz. Bunu yüzlerce dosya için yapmam gerekiyor, bu yüzden manuel olarak düzeltemiyorum.

Bununla ilgilenen herhangi bir kabuk yerleşik var mı?


symlinksMutlak bağlantıları göreceli olarak dönüştürmek için kullanabilirsiniz .
Stéphane Chazelas

@StephaneChazelas nasıl? Açıklayan bir cevap gönderebilir misiniz?
terdon

Yanıtlar:


10

symlinksMutlak yolları göreceli dönüştürmek için komutu kullanabilirsiniz :

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

Mutlak bağlantılarımız var, haydi onları göreceliğe dönüştürelim:

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

Referanslar


Teşekkürler Stephane. Bu kök sorunu çözdü, ben de kabul ettim. Bununla birlikte, iki yolu (kaynak ve hedef) alan ve kaynaktan hedefe giden göreceli yolu çıkaran (burada sembolik bağlantılar yok) bir şeyin (örneğin, bir zsh işlevi) olması harika olurdu. Bu da başlıkta sorulan soruyu çözecektir.
Amelio Vazquez-Reina


24

realpathKomutu kullanmayı deneyin (GNU'nun bir kısmı coreutils; > = 8.23 ), örneğin:

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

MacOS kullanıyorsanız, GNU sürümünü şu şekilde yükleyin: brew install coreutilsve kullanın grealpath.

Komutun başarılı olması için her iki yolun da mevcut olması gerektiğini unutmayın. Göreli yola yine de ihtiyacınız varsa bunlardan biri olmasa bile -m anahtarını ekleyin.

Daha fazla örnek için bkz . Geçerli bir dizin verilen mutlak yolu göreceli yola dönüştürme .


Anladım realpath: unrecognized option '--relative-to=.':( realpath sürüm 1.19
phuclv

@ LưuVĩnhPhúc GNU / Linux versiyonunu kullanmanız gerekir realpath. MacOS'taysanız: ile yükleyin brew install coreutils.
kenorb

hayır, Ubuntu'dayım
14.04

@ LưuVĩnhPhúc realpath (GNU coreutils) 8.26Parametrenin bulunduğu yerdeyim . Bakınız: gnu.org/software/coreutils/realpath Bir takma ad kullanmadığınızı (örn. Farklı çalıştırma \realpath) ve sürümü bu sürümden nasıl yükleyebileceğinizi iki kez kontrol edin coreutils.
kenorb


7

Bence bu python çözümü (@ EvanTeitelman'ın cevabıyla aynı yazıdan alınmış) da bahsetmeye değer. Şunu da ekle ~/.zshrc:

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

Daha sonra yapabilirsiniz, örneğin:

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

@StephaneChazelas bu bağlantılı cevaptan doğrudan bir kopya yapıştı. Ben bir Perl çocuğuyum ve aslında hiç piton tanımadığımdan nasıl kullanacağımı bilmiyorum sys.argv. Gönderilen kod yanlışsa veya iyileştirilebilirse, teşekkür ederim.
terdon

@StephaneChazelas Şimdi anlıyorum, düzenleme için teşekkürler.
terdon

7

Bununla ilgilenecek herhangi bir kabuk yerleşik yoktur. Yine de Stack Overflow'ta bir çözüm buldum . Çözümü burada ufak değişikliklerle kopyaladım ve bu cevabı topluluk wiki olarak işaretledim.

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

Bu işlevi aşağıdaki gibi kullanabilirsiniz:

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

Bu, soruna en basit ve en güzel çözümdür.

Ayrıca, tüm burne benzeri kabuklarda çalışmalıdır.

Ve burada bilgelik: kesinlikle harici araçlara ve hatta karmaşık koşullara ihtiyaç yoktur. Bir çift önek / sonek ikamesi ve bir süre döngüsü, bourne benzeri kabuklarda yapılan hemen hemen her şeyi elde etmeniz için yeterlidir.

Ayrıca PasteBin aracılığıyla da erişilebilir: http://pastebin.com/CtTfvime


Çözümünüz için teşekkürler. Bunu yapmak için bir Makefilesatırda çift tırnak pos=${pos%/*}(ve tabii ki, her satırın sonunda noktalı virgül ve ters eğik çizgiler) eklemek için gerekli bir i buldum.
Circonflexe

Fikir çalışmayan vakaların güncel sürümü bol, fena değil ama: relpath /tmp /tmp, relpath /tmp /tmp/a, relpath /tmp/a /. Ek olarak, tüm yolların mutlak VE kanonik hale getirilmesi gerektiğini (yani /tmp/a/..çalışmaz)
belirtmeyi unutmuşsunuz

0

Bunu güvenilir bir şekilde yapmak için bir bash betiği yazmak zorunda kaldım (ve elbette dosyanın varlığını doğrulamadan).

kullanım

relpath <symlink path> <source path>

Göreceli bir kaynak yolunu döndürür.

örnek:

#/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

ikisi de olsun ../../CC/DD/EE


Hızlı bir test yapmak için koşabilirsiniz

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

sonuç: test listesi

/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, bu betiği kaydetmeden kullanmak istiyorsanız ve bu betiğe güveniyorsanız, bunun için bash hilesiyle iki yolunuz vardır.

relpathKomutu izleyerek bash işlevi olarak içe aktarın . (Bash 4+ iste)

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

veya betiği anında bukle bash combo ile olduğu gibi çağırın.

curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- /a/b/c/d/e /a/b/CC/DD/EE
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.