web-dev-qa-db-fra.com

Conversion d'un chemin relatif en chemin absolu?

Je ne sais pas si ces chemins sont des doublons. Compte tenu du chemin relatif, comment puis-je déterminer le chemin absolu à l'aide d'un script Shell?

Exemple:

relative path: /x/y/../../a/b/z/../c/d

absolute path: /a/b/c/d
55
josh

De cette source vient:

#!/bin/bash

# Assume parameter passed in is a relative path to a directory.
# For brevity, we won't do argument type or length checking.

ABS_PATH=`cd "$1"; pwd` # double quotes for paths that contain spaces etc...
echo "Absolute path: $ABS_PATH"

Vous pouvez également faire une doublure Perl, par exemple en utilisant Cwd::abs_path

48
DVK

La méthode la plus fiable que j'ai rencontrée sous Unix est readlink -f:

$ readlink -f /x/y/../../a/b/z/../c/d
/a/b/c/d

Quelques mises en garde:

  1. Cela a également pour effet secondaire de résoudre tous les liens symboliques. Cela peut être souhaitable ou non, mais c'est généralement le cas.
  2. readlink donnera un résultat vide si vous référencez un répertoire inexistant. Si vous souhaitez prendre en charge des chemins inexistants, utilisez readlink -m au lieu. Malheureusement, cette option n'existe pas sur les versions de readlink publiées avant ~ 2005.
51
bukzor

Utilisation de bash

# Directory
relative_dir="folder/subfolder/"
absolute_dir="$( cd "$relative_dir" && pwd )"

# File
relative_file="folder/subfolder/file"
absolute_file="$( cd "${relative_file%/*}" && pwd )"/"${relative_file##*/}"
  • ${relative_file%/*} Est le même résultat que dirname "$relative_file"
  • ${relative_file##*/} Est le même résultat que basename "$relative_file"

Mises en garde : Ne résout pas les liens symboliques (c'est-à-dire ne canonise pas le chemin) => Peut ne pas différencier tous les doublons si vous utilisez des liens symboliques.


Utilisation de realpath

La commande realpath fait le travail. Une alternative consiste à utiliser readlink -e (Ou readlink -f). Cependant, realpath n'est pas souvent installé par défaut. Si vous ne pouvez pas être sûr que realpath ou readlink est présent, vous pouvez le remplacer en utilisant Perl (voir ci-dessous).


Utilisation de Perl

Steven Kramer propose un alias Shell si realpath n'est pas disponible dans votre système:

$ alias realpath="Perl -MCwd -e 'print Cwd::realpath(\$ARGV[0]),qq<\n>'"
$ realpath path/folder/file
/home/user/absolute/path/folder/file

ou si vous préférez utiliser directement Perl:

$ Perl -MCwd -e 'print Cwd::realpath($ARGV[0]),qq<\n>' path/folder/file
/home/user/absolute/path/folder/file

Cette commande Perl sur une ligne utilise Cwd::realpath. Il existe en fait trois fonctions Perl. Ils prennent un seul argument et renvoient le chemin absolu. Les détails ci-dessous proviennent de la documentation Perl5> Core modules> Cwd .

  • abs_path() utilise le même algorithme que getcwd(). Les liens symboliques et les composants de chemin relatif (. Et ..) Sont résolus pour renvoyer le nom de chemin canonique, tout comme realpath .

    use Cwd 'abs_path';
    my $abs_path = abs_path($file);
    
  • realpath() est un synonyme de abs_path()

    use Cwd 'realpath';
    my $abs_path = realpath($file);
    
  • fast_abs_path() est une version plus dangereuse mais potentiellement plus rapide de abs_path()

    use Cwd 'fast_abs_path';
    my $abs_path = fast_abs_path($file);
    

Ces fonctions sont exportées uniquement sur demande => utilisez donc Cwd pour éviter l'erreur "Sous-programme non défini" comme indiqué par arielf . Si vous souhaitez importer ces trois fonctions, vous pouvez utiliser une seule ligne use Cwd:

use Cwd qw(abs_path realpath fast_abs_path);
19
olibre

Jetez un œil à "realpath".

$ realpath

usage: realpath [-q] path [...]

$ realpath ../../../../../

/data/home
18
SteveMc

Depuis que je l'ai rencontré plusieurs fois au fil des ans, et cette fois-ci, j'avais besoin d'une version portable bash pure que je pouvais utiliser sur OSX et linux, j'ai continué et j'en ai écrit une:

La version vivante vit ici:

https://github.com/keen99/Shell-functions/tree/master/resolve_path

mais pour le bien de SO, voici la version actuelle (je pense qu'elle est bien testée .. mais je suis ouvert aux commentaires!)

Cela pourrait ne pas être difficile de le faire fonctionner pour Shell Bourne ordinaire (sh), mais je n'ai pas essayé ... J'aime trop $ FUNCNAME. :)

#!/bin/bash

resolve_path() {
    #I'm bash only, please!
    # usage:  resolve_path <a file or directory> 
    # follows symlinks and relative paths, returns a full real path
    #
    local owd="$PWD"
    #echo "$FUNCNAME for $1" >&2
    local opath="$1"
    local npath=""
    local obase=$(basename "$opath")
    local odir=$(dirname "$opath")
    if [[ -L "$opath" ]]
    then
    #it's a link.
    #file or directory, we want to cd into it's dir
        cd $odir
    #then extract where the link points.
        npath=$(readlink "$obase")
        #have to -L BEFORE we -f, because -f includes -L :(
        if [[ -L $npath ]]
         then
        #the link points to another symlink, so go follow that.
            resolve_path "$npath"
            #and finish out early, we're done.
            return $?
            #done
        Elif [[ -f $npath ]]
        #the link points to a file.
         then
            #get the dir for the new file
            nbase=$(basename $npath)
            npath=$(dirname $npath)
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        Elif [[ -d $npath ]]
         then
        #the link points to a directory.
            cd "$npath"
            ndir=$(pwd -P)
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition inside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            echo "npath [[ $npath ]]" >&2
            return 1
        fi
    else
        if ! [[ -e "$opath" ]]
         then
            echo "$FUNCNAME: $opath: No such file or directory" >&2
            return 1
            #and break early
        Elif [[ -d "$opath" ]]
         then 
            cd "$opath"
            ndir=$(pwd -P)
            retval=0
            #done
        Elif [[ -f "$opath" ]]
         then
            cd $odir
            ndir=$(pwd -P)
            nbase=$(basename "$opath")
            retval=0
            #done
        else
            echo "$FUNCNAME: ERROR: unknown condition outside link!!" >&2
            echo "opath [[ $opath ]]" >&2
            return 1
        fi
    fi
    #now assemble our output
    echo -n "$ndir"
    if [[ "x${nbase:=}" != "x" ]]
     then
        echo "/$nbase"
    else 
        echo
    fi
    #now return to where we were
    cd "$owd"
    return $retval
}

voici un exemple classique, grâce à brew:

%% ls -l `which mvn`
lrwxr-xr-x  1 draistrick  502  29 Dec 17 10:50 /usr/local/bin/mvn@ -> ../Cellar/maven/3.2.3/bin/mvn

utilisez cette fonction et elle renverra le chemin -real-:

%% cat test.sh
#!/bin/bash
. resolve_path.inc
echo
echo "relative symlinked path:"
which mvn
echo
echo "and the real path:"
resolve_path `which mvn`


%% test.sh

relative symlinked path:
/usr/local/bin/mvn

and the real path:
/usr/local/Cellar/maven/3.2.3/libexec/bin/mvn
1
keen

Peut-être que cela aide:

$path = "~user/dir/../file" 
$resolvedPath = glob($path); #   (To resolve paths with '~')
# Since glob does not resolve relative path, we use abs_path 
$absPath      = abs_path($path);
0
Rohan