web-dev-qa-db-fra.com

Obtenir des liens relatifs entre deux chemins

Dis que j'ai deux chemins: <source_path> et <target_path>. Je voudrais que ma coquille (ZSH) puisse automatiquement savoir s'il existe un moyen de représenter <target_path> de <source_path> comme un chemin relatif.

Par exemple. Assumons

  • <source_path> est /foo/bar/something
  • <target_path> est /foo/hello/world

Le résultat serait ../../hello/world

Pourquoi j'ai besoin de ceci:

J'ai besoin de créer un lien symbolique de <source_path> à <target_path> Utilisation d'un relatif liaison symbolique dans la mesure du possible, car notre serveur Samba n'affiche pas correctement le fichier lorsque j'accumule ces fichiers sur le réseau à partir de Windows (je ne suis pas l'administrateur SYS, et ne le faites pas avoir le contrôle de ce réglage)

En admettant que <target_path> et <source_path> sont des chemins absolus, ce qui suit crée un lien symbolique pointant vers un chemin absol.

ln -s <target_path> <source_path>

donc, cela ne fonctionne pas pour mes besoins. J'ai besoin de le faire pour des centaines de fichiers, donc je ne peux pas simplement le réparer manuellement.

Toute coquille intégrée qui s'occupe de cela?

25

Vous pouvez utiliser la commande symlinks pour convertir des chemins absolus en relatif:

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

Nous avons des liens absolus, les convertissons en relatif:

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

Les références

10
Stéphane Chazelas

Essayez d'utiliser realpath commande (une partie de GNU coreutils; > = 8.23 ​​), par exemple:

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

Si vous utilisez des macos, install GNU Version via: brew install coreutils et utiliser grealpath.

Notez que les deux chemins doivent exister pour que la commande réussisse. Si vous avez besoin du chemin relatif de toute façon, même si l'un d'entre eux n'existe pas, ajoutez le commutateur -M.

Pour plus d'exemples, voir Convertir un chemin absolu en chemin relatif donné un répertoire actuel .

27
kenorb

Il n'y a pas de shell comité à prendre en charge cela. J'ai trouvé ne solution sur le débordement de pile cependant. J'ai copié la solution ici avec de légères modifications et j'ai marqué cette réponse en tant que wiki communautaire.

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

Vous pouvez utiliser cette fonction comme si:

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

Je pense que ceci python solution (extrait du même poste que la réponse de @ Evaneitelman) mérite également de mentionner. Ajoutez ceci à votre ~/.zshrc:

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

Vous pouvez alors faire, par exemple:

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

Je devais écrire un Bash Script pour faire cela de manière fiable (et bien sûr sans vérifier l'existence de fichiers).

Usage

relpath <symlink path> <source path>

Retourne un chemin source relatif.

exemple:

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

les deux obtiennent ../../CC/DD/EE


Avoir un test rapide, vous pouvez exécuter

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

résultat: une liste de 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, si vous souhaitez utiliser ce script sans l'enregistrer et que vous faites confiance à ce script, vous avez deux façons de ce faire avec un tour de bash.

Import relpath comme fonction Bash en suivant la commande suivante. (Exiger Bash 4+)

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

ou appelez le script sur la mouche comme avec 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
0
osexp2003