Convertire il percorso relativo in un percorso assoluto?

Non sono sicuro che questi percorsi siano duplicati. Dato il percorso relativo, come posso determinare il percorso assoluto usando uno script di shell?

Esempio:

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

Da questa fonte arriva:

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

Puoi anche fare una Cwd::abs_path Perl, ad esempio usando Cwd::abs_path

Il metodo più affidabile che ho trovato in unix è readlink -f :

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

Un paio di avvertimenti:

  1. Questo ha anche l’effetto collaterale di risolvere tutti i collegamenti simbolici. Questo può o non può essere desiderabile, ma di solito lo è.
  2. readlink fornirà un risultato vuoto se si fa riferimento a una directory inesistente. Se si desidera supportare percorsi inesistenti, utilizzare invece readlink -m . Sfortunatamente questa opzione non esiste sulle versioni di readlink rilasciate prima di ~ 2005.

Dai un’occhiata a ‘realpath’.

 $ realpath usage: realpath [-q] path [...] $ realpath ../../../../../ /data/home 

Usando 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%/*} è lo stesso risultato di dirname "$relative_file"
  • ${relative_file##*/} è lo stesso risultato di basename "$relative_file"

Avvertenze : non risolve i collegamenti simbolici (cioè non canonizza il percorso) => Non può differenziare tutti i duplicati se si utilizzano collegamenti simbolici.


Usando realpath

Command realpath fa il lavoro. Un’alternativa è usare readlink -e (o readlink -f ). Tuttavia realpath non è spesso installato di default. Se non puoi essere sicuro che sia presente realpath o readlink , puoi sostituirlo usando perl (vedi sotto).


Usando perl

Steven Kramer propone un alias di shell se realpath non è disponibile nel tuo sistema:

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

o se preferisci usare direttamente perl:

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

Questo comando perl a riga Cwd::realpath utilizza Cwd::realpath . Ci sono in effetti tre funzioni perl. Prendono un singolo argomento e restituiscono il percorso assoluto. Di seguito i dettagli della documentazione Perl5> Moduli principali> Cwd .

  • abs_path() utilizza lo stesso algoritmo di getcwd() . I collegamenti simbolici e i componenti relativi al percorso ( . E .. ) sono risolti per restituire il nome del percorso canonico, proprio come realpath .

     use Cwd 'abs_path'; my $abs_path = abs_path($file); 
  • realpath() è un sinonimo di abs_path()

     use Cwd 'realpath'; my $abs_path = realpath($file); 
  • fast_abs_path() è una versione più pericolosa, ma potenzialmente più veloce di abs_path()

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

Queste funzioni vengono esportate solo su richiesta => quindi utilizzare Cwd per evitare l’errore “subroutine non Cwd come indicato da arielf . Se si desidera importare tutte queste tre funzioni, è ansible utilizzare una riga use Cwd :

 use Cwd qw(abs_path realpath fast_abs_path); 

Da quando mi sono imbattuto in questo molte volte nel corso degli anni, e questa volta ho avuto bisogno di una versione bash portatile che potessi usare su OSX e Linux, sono andato avanti e ne ho scritto uno:

La versione vivente vive qui:

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

ma per il bene di SO, ecco la versione corrente (mi sembra che sia ben collaudata … ma sono aperto al feedback!)

Potrebbe non essere difficile farlo funzionare per plain bourne shell (sh), ma non ho provato … Mi piace troppo $ FUNCNAME. 🙂

 #!/bin/bash resolve_path() { #I'm bash only, please! # usage: resolve_path  # 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 } 

ecco un esempio classico, grazie a brew:

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

usa questa funzione e restituirà il percorso -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 

Può essere questo aiuta:

 $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);