Conversione del percorso relativo in percorso assoluto senza collegamento simbolico


63

Esiste un comando Unix per ottenere il percorso assoluto (e canonicalizzato) da un percorso relativo che può contenere collegamenti simbolici?

Risposte:


80

È possibile utilizzare l' readlinkutilità, con l' -fopzione:

-f, --canonicalize

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

Alcune distribuzioni, ad esempio quelle che usano coreutils GNU e FreeBSD , hanno anche realpath(1)un'utility che praticamente chiama realpath(3)e fa praticamente la stessa cosa.


17
Lo -fswitch non esiste su Mac OS X (almeno a partire dal 10.8.5). Senza l' -finterruttore restituisce solo un codice di errore.
iconoclasta,

4
Alcuni sistemi sono dotati né readlinkrealpath- Solaris e HP-UX, in particolare.
Sildoreth,

Per il problema Mac: brew install coreutils apple.stackexchange.com/a/69332/23040 e se non si desidera eseguire il passaggio che reindirizza tutti gli strumenti della CLI Mac standard agli equivalenti GNU appena installati, è sufficiente chiamare greadlink.
Adrian,

19

Portabilmente, la PWDvariabile è impostata dalla shell in una posizione assoluta della directory corrente. Qualsiasi componente di quel percorso può essere un collegamento simbolico.

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

Se si desidera eliminare .e ..anche, passare alla directory contenente il file e ottenere $PWDlì:

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

Non esiste un modo portatile per seguire i collegamenti simbolici. Se si dispone di un percorso per una directory, nella maggior parte dei casi unices $(cd -- "$dir" && pwd -P 2>/dev/null || pwd)fornisce un percorso che non utilizza collegamenti simbolici, poiché le shell che tracciano collegamenti simbolici tendono ad implementarsi pwd -P("P" per "fisico").

Alcuni unices forniscono un'utilità per stampare il percorso "fisico" di un file.

  • I sistemi Linux abbastanza recenti (con coreutils GNU o BusyBox) hanno readlink -f, come pure FreeBSD ≥8.3, NetBSD ≥4.0 e OpenBSD fino al 2.2.
  • FreeBSD ≥4.3 ha realpath(è presente anche su alcuni sistemi Linux, ed è in BusyBox).
  • Se Perl è disponibile, è possibile utilizzare il Cwdmodulo .

    perl -MCwd -e 'print Cwd::realpath($ARGV[0])' path/to/file

Perché installi qualcosa in pwd( $(cd -- "$dir" && pwd -P 2>/dev/null | pwd))? Non sembra interessarsi allo stdin.
Mateusz Piotrowski,

1
@MateuszPiotrowski Typo, intendevo scrivere||
Gilles 'SO- smetti di essere cattivo'

5

È pwdadatto alle tue esigenze? Fornisce il percorso assoluto della directory corrente. O forse quello che vuoi è realpath () .


3

alistere rss67in questo articolo introdurre il modo più stabile, compatibile e più semplice. Non ho mai visto un modo migliore di questo prima.

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

Se vuoi tornare alla posizione originale,

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

o

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

Vorrei che questo aiuti. Questa è stata la soluzione più grande per me.


12
In realtà non è un buon modo. Il passaggio da una directory a un'altra non è affidabile: potresti non avere i permessi per tornare indietro o la directory potrebbe essere stata rinominata nel frattempo. Invece, cambiare le directory in una subshell: ABSPATH=$(cd -- "$RELPATH" && pwd). Nota le doppie virgolette intorno alla sostituzione (in modo da non interrompere se il percorso contiene spazi bianchi e caratteri globbing) e il --(nel caso in cui il percorso inizi con -). Inoltre, questo funziona solo per le directory e non può canonicalizzare i collegamenti simbolici come richiesto. Vedi la mia risposta per maggiori dettagli.
Gilles 'SO- smetti di essere cattivo' il

"> potresti non avere il permesso di tornare" Intendi cd in una directory ma non riesci a tornare indietro? Potresti fornire alcuni scenari di esempio.
Talespin_Kit

0

Le altre risposte qui andavano bene ma erano insufficienti per le mie esigenze. Avevo bisogno di una soluzione che potessi usare nei miei script su qualsiasi macchina. La mia soluzione era quella di scrivere uno script shell che posso invocare dagli script dove ne ho bisogno.

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

Questa soluzione è stata scritta per la Bourne Shell per la portabilità. Ho questo in uno script chiamato "respath.sh".

Questo script può essere utilizzato in questo modo:

respath.sh '/path/to/file'

O così

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

Lo script risolve il percorso nel primo argomento utilizzando il percorso del secondo argomento come punto di partenza. Se viene fornito solo il primo argomento, lo script risolve il percorso relativo alla directory di lavoro corrente.


Nota che probabilmente troverai solo una shell Bourne su Solaris 10 e precedenti al giorno d'oggi. Quella shell Bourne come la maggior parte delle implementazioni della shell Bourne da quando SVR2 (1984) ha pwdincorporato e non supporta -P(la shell Bourne non ha implementato la gestione logica $ PWD come fanno le shell POSIX).
Stéphane Chazelas,

Se si abbandona la compatibilità Bourne, è possibile utilizzare la sintassi POSIX $ {var ## pattern} ed evitare quelle echo | sed che si romperanno con alcuni valori.
Stéphane Chazelas,

Dimentica di controllare lo stato di uscita di cd (e pwd). Probabilmente dovresti usare cd -P --anche nel caso in cui il 1 ° argomento contenga alcuni ..
Stéphane Chazelas,

Il tuo casecontrollo dovrebbe essere ..|../*)invece di ..*.
Stéphane Chazelas,

C'è un caso di citazioni mancanti indirname $cwd
Stéphane Chazelas

0

Nel caso, in aggiunta, hai un percorso predefinito per $1(di solito ${PWD}), il seguente funzionerebbe:

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

0

Rispettando la risposta di tutti gli altri, ho trovato la seguente funzione molto più semplice e portatile tra Linux / MAC OSX rispetto ad altre che ho trovato ovunque. Potrebbe aiutare qualcuno.

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

-1

Supponiamo che la variabile a memorizzi un nome di percorso (a = "qualunque")

1. Per un potenziale percorso contenente spazio:

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

2. Per la vera domanda, ovvero convertire il percorso in assoluto: readlink può recuperare i contenuti del link simbolico, che può essere impiegato come segue. (la nota readlink -fsarebbe stata perfetta ma non è disponibile in almeno Mac OS X bash, in cui -f sta per FORMAT .)

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

3. Appena appreso pwd -Pin man pwd: -P è " Visualizza la directory di lavoro corrente fisica (tutti i collegamenti simbolici risolti) " e quindi

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

deve funzionare anche.

Conclusione: combinare 1 con 2 per soddisfare entrambe le vostre esigenze, mentre i nomi di percorso contenenti spazio sono già curati in 3 più concisi .


Hai sbagliato. Ad esempio, se il percorso include uno spazio, la maggior parte di quanto sopra non funzionerà.
Anthon,

o un carattere globale a volte. E non risolve i collegamenti simbolici, come richiesto.
dave_thompson_085,

Ho eliminato la mia risposta "commento" per quanto riguarda i nomi dei percorsi contenenti spazi e modificato di conseguenza la parte "risposta".
vxtal
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.