Conversion du chemin relatif en chemin absolu sans lien symbolique

63

Existe-t-il une commande Unix pour obtenir le chemin absolu (et canonique) à partir d’un chemin relatif pouvant contenir des liens symboliques?

Benjamin
la source

Réponses:

80

Vous pouvez utiliser l' readlinkutilitaire, avec l' -foption:

-f, --canonicalize

      canonicalize  by  following  every symlink in every component of
      the given name recursively; all  but  the  last  component  must
      exist

Certaines distributions, par exemple celles qui utilisent GNU coreutils et FreeBSD , sont également fournies avec un realpath(1)utilitaire qui appelle realpath(3)et fait à peu près la même chose.

Tapis
la source
17
Le -fcommutateur n'existe pas sur Mac OS X (au moins à partir de 10.8.5). Sans le -fcommutateur, il ne fait que renvoyer un code d'erreur.
iconoclaste
4
Certains systèmes sont livrés avec ni readlinkni realpath- Solaris et HP-UX, en particulier.
Sildoreth
Pour le problème Mac: brew install coreutils apple.stackexchange.com/a/69332/23040 , et si vous ne souhaitez pas effectuer l'étape qui redirige tous les outils CLI du Mac standard vers les équivalents GNU nouvellement installés, appelez simplement greadlink.
Adrian
19

De manière portable, la PWDvariable est définie par le shell sur un emplacement absolu du répertoire actuel. Tout composant de ce chemin peut être un lien symbolique.

case $f in
  /*) absolute=$f;;
  *) absolute=$PWD/$f;;
esac

Si vous voulez éliminer .et ..aussi bien, allez dans le répertoire contenant le fichier et obtenez- $PWDy:

if [ -d "$f" ]; then f=$f/.; fi
absolute=$(cd "$(dirname -- "$f")"; printf %s. "$PWD")
absolute=${absolute%?}
absolute=$absolute/${f##*/}

Il n'y a pas de moyen portable de suivre des liens symboliques. Si vous avez un chemin d'accès à un répertoire, alors sur la plupart des ordinateurs, $(cd -- "$dir" && pwd -P 2>/dev/null || pwd)un chemin n'utilisant pas de liens symboliques, car les shells qui suivent les liens symboliques ont tendance à être implémentés pwd -P(«P» pour «physique»).

Certains ordinateurs fournissent un utilitaire pour imprimer le chemin «physique» vers un fichier.

  • Les systèmes Linux raisonnablement récents (avec GNU coreutils ou BusyBox) ont readlink -f, tout comme FreeBSD ≥8.3, NetBSD ≥4,0 et OpenBSD dès la version 2.2.
  • FreeBSD ≥4.3 a realpath(il est également présent sur certains systèmes Linux, et il est dans BusyBox).
  • Si Perl est disponible, vous pouvez utiliser le Cwdmodule .

    perl -MCwd -e 'print Cwd::realpath($ARGV[0])' path/to/file
Gilles, arrête de faire le mal
la source
Pourquoi insérez-vous quelque chose dans pwd( $(cd -- "$dir" && pwd -P 2>/dev/null | pwd))? Il ne semble pas se soucier de stdin.
Mateusz Piotrowski
1
@MateuszPiotrowski Typo, j'avais l'intention d'écrire||
Gilles 'SO, arrête d'être méchant'
5

Est pwdadapté à vos besoins? Il donne le chemin absolu du répertoire courant. Ou peut-être que ce que vous voulez, c'est realpath () .

Xanpeng
la source
3

alisteret rss67dans cet article, introduisez le moyen le plus stable, compatible et le plus simple. Je n'ai jamais vu de meilleure façon que cela avant.

RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`

Si vous voulez revenir à l'emplacement d'origine,

ORGPATH=`pwd`
RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`
cd $ORGPATH

ou

RELPATH=./../
cd $RELPATH
ABSPATH=`pwd`
cd -

Je souhaite que cela aide. C'était la meilleure solution pour moi.

Eonil
la source
12
Ce n'est pas vraiment un bon moyen. Le changement de répertoire et le retour ne sont pas fiables: vous n’avez peut-être pas l’autorisation de revenir, ou le répertoire a peut-être été renommé entre-temps. , Changer la place des répertoires dans un sous - shell: ABSPATH=$(cd -- "$RELPATH" && pwd). Notez les guillemets autour de la substitution (afin de ne pas rompre si le chemin contient des espaces et des caractères globulants) et le --(dans le cas où le chemin commence par -). De plus, cela ne fonctionne que pour les répertoires et ne canonise pas les liens symboliques comme demandé. Voir ma réponse pour plus de détails.
Gilles 'SO- arrête d'être méchant' le
"> vous pourriez ne pas avoir la permission de revenir" Vous voulez dire cd dans un répertoire mais vous ne pouvez pas cd en arrière? Pourriez-vous s'il vous plaît fournir quelques exemples de scénarios.
Talespin_Kit
0

Les autres réponses ici étaient bien mais étaient insuffisantes pour mes besoins. J'avais besoin d'une solution que je pourrais utiliser dans mes scripts sur n'importe quelle machine. Ma solution a été d'écrire un script shell que je peux invoquer à partir des scripts où j'en ai besoin.

#!/bin/sh
if [ $# -eq 0 ] || [ $# -gt 2 ]; then
  printf 'Usage: respath path [working-directory]\n' >&2
  exit 1
fi
cantGoUp=

path=$1
if [ $# -gt 1 ]; then
  cd "$2"
fi
cwd=`pwd -P` #Use -P option to account for directories that are actually symlinks

#Handle non-relative paths, which don't need resolution
if echo "$path" | grep '^/' > /dev/null ; then
  printf '%s\n' "$path"
  exit 0
fi

#Resolve for each occurrence of ".." at the beginning of the given path.
#For performance, don't worry about ".." in the middle of the path.
while true
do
  case "$path" in
    ..*)
      if [ "$cwd" = '/' ]; then
        printf 'Invalid relative path\n' >&2
        exit 1
      fi
      if [ "$path" = '..' ]; then
        path=
      else
        path=`echo "$path" | sed 's;^\.\./;;'`
      fi
      cwd=`dirname $cwd`
      ;;
    *)
      break
      ;;
  esac
done

cwd=`echo "$cwd" | sed 's;/$;;'`
if [ -z "$path" ]; then
  if [ -z "$cwd" ]; then
    cwd='/'
  fi
  printf '%s\n' "$cwd"
else
  printf '%s/%s\n' "$cwd" "$path"
fi

Cette solution est écrite pour le shell Bourne pour la portabilité. J'ai ceci dans un script nommé "respath.sh".

Ce script peut être utilisé comme ceci:

respath.sh '/path/to/file'

Ou comme ça

respath.sh '/relative/path/here' '/path/to/resolve/relative/to'

Le script résout le chemin dans le premier argument en utilisant le chemin du deuxième argument comme point de départ. Si seul le premier argument est fourni, le script résout le chemin relatif à votre répertoire de travail actuel.

Sildoreth
la source
Notez que vous ne trouverez probablement un shell Bourne que sous Solaris 10 et versions antérieures. Ce shell Bourne, comme la plupart des implémentations de shell Bourne depuis que SVR2 (1984) a été pwdintégré et ne prend pas en charge -P(le shell Bourne n’a pas implémenté cette gestion logique $ PWD comme le font les shell POSIX).
Stéphane Chazelas
Si vous supprimez la compatibilité Bourne, vous pouvez utiliser la syntaxe POSIX $ {var ## pattern} et éviter les échos qui vont rompre avec certaines valeurs.
Stéphane Chazelas
Votre oublier de vérifier le statut de sortie de cd (et pwd). Vous devriez probablement l'utiliser cd -P --aussi au cas où le premier argument en contiendrait ..
Stéphane Chazelas
Votre casechèque devrait être à la ..|../*)place de ..*.
Stéphane Chazelas
Il y a un cas de citations manquantes dansdirname $cwd
Stéphane Chazelas
0

Si, en outre, vous avez un chemin prédéfini pour $1(généralement ${PWD}), ceci fonctionne:

CWD="${1:-${PredefinedAbsolutePath}}"
if [ "${CWD}" != "${PredefinedAbsolutePath}}" ]; then
    case $1 in
        /*) echo "CWD=${CWD}"; ;;
        *) CWD=$(realpath $1); echo "CWD=${CWD}"; ;;
    esac
fi
Nikhil
la source
0

En respectant les réponses de tous les autres, j’ai trouvé la fonction ci-dessous très facile et portable entre Linux / MAC OSX et d’autres que j’ai trouvée partout. Peut aider quelqu'un.

#!/usr/bin/bash
get_absolute_path(){
    file_path=`pwd $1`
    echo "$file_path/$1"
}
#to assign to a variable
absolute_path=$(get_absolute_path "file.txt")
echo $absolute_path
Lauksas
la source
-1

Supposons que la variable a stocke un nom de chemin (a = "quel que soit")

1. Pour un chemin potentiel contenant de l’espace:

c=$(grep -o " " <<< $a | wc -l); if (( $c > "0" )); then a=$(sed 's/ /\\ /g' <<< $a); fi

2. Pour la question réelle, c'est-à-dire la conversion du chemin en absolu: readlink peut extraire le contenu d'un lien symbolique, ce qui peut être utilisé comme suit. (la note readlink -faurait été parfaite mais n’est pas disponible dans au moins Mac OS X bash, où -f signifie FORMATS .)

if [[ $(readlink $a) == "" ]]; then a=$(cd $a; pwd); else a=$(cd $(readlink $a); pwd); fi

3. Vient d’apprendre pwd -Pdans man pwd: -P consiste à " Afficher le répertoire de travail physique actuel (tous les liens symboliques résolus) " "et donc

if (( $(grep -o " " <<< $a | wc -l) > "0" )); then a=$(sed 's/ /\\ /g' <<< $a); fi; a=$(cd $a; pwd -P)

doit travailler aussi.

Conclusion: combinez 1 et 2 pour satisfaire vos exigences, tandis que les noms de chemins contenant des espaces sont déjà pris en charge dans le 3, plus concis .

vxtal
la source
Vous vous trompez. Par exemple, si votre chemin comprend un espace, la plupart des éléments précédents ne fonctionneront pas.
Anthon
ou un char glob parfois. Et cela ne résout pas les liens symboliques, comme cela a été demandé.
dave_thompson_085
J'ai supprimé ma réponse "commentaire" concernant les noms de chemin contenant des espaces et modifié la partie "réponse" en conséquence.
vxtal