2010-11-13 52 views
70

Esiste un comando per recuperare il percorso assoluto dato il percorso relativo?Bash: recupera il percorso assoluto dato relativo

Ad esempio voglio $ line per contenere il percorso assoluto di ogni file in dir ./etc/

find ./ -type f | while read line; do 
    echo $line 
done 
+4

possibile duplicato di [Conversione del percorso relativo in percorso assoluto] (http://stackoverflow.com/questions/4045253/converting-relative-path-into-absolute-path) o [this] (http: // stackoverflow. com/domande/2564634/bash-convert-assoluto-path-in-parente-path-dato-a-corrente-directory). –

+1

non duplicato, ma simile – mpapis

+1

possibile duplicato di [comando bash/fish per stampare il percorso assoluto su un file] (http: // stackoverflow.it/questions/3915040/bash-fish-command-to-print-absolute-path-to-a-file) –

risposta

35

uso:

find $(pwd)/ -type f 

per ottenere tutti file o

echo $(pwd)/$line 

per visualizzare il percorso completo (se percorso relativo conta per)

+1

E se specificassi un percorso relativo nel find ?? – nubme

+7

quindi vedi i link nel commento alla tua domanda, il modo migliore è probabilmente 'readlink -f $ line' – mpapis

+0

il tuo giusto findlink ha funzionato al meglio =] grazie – nubme

2

In caso di find, è probabilmente più facile da fare solo il percorso assoluto per la sua ricerca in, ad esempio:

find /etc 
find `pwd`/subdir_of_current_dir/ -type f 
0

Quello che hanno detto, ad eccezione find $PWD o (in bash) find ~+ è un po 'più conveniente.

77

Se avete il pacchetto coreutils installato è possibile in genere utilizzare readlink -f relative_file_name al fine di recuperare quella assoluta (con tutti i collegamenti simbolici risolti)

+11

Purtroppo questo non funziona su Mac :( –

+1

Il comportamento per questo è un po ' diverso da quello che chiede l'utente, seguirà e risolverà i collegamenti simbolici ricorsivi ovunque nel percorso. Potresti non volerlo in alcuni casi. – ffledgling

26

Per quello che vale, ho votato per la risposta che è stato raccolto, ma ha voluto condividere una soluzione. Il rovescio della medaglia è, è solo Linux - ho impiegato circa 5 minuti per cercare l'equivalente OSX prima di arrivare allo stack overflow. Sono sicuro che sia là fuori comunque.

Su Linux è possibile utilizzare readlink -e in tandem con dirname.

$(dirname $(readlink -e ../../../../etc/passwd)) 

cede

/etc/ 

e quindi si utilizza la sorella dirname s', basename di ottenere solo il nome del file

$(basename ../../../../../passwd) 

rendimenti

passwd 

Mettere tutto insieme ..

F=../../../../../etc/passwd 
echo "$(dirname $(readlink -e $F))/$(basename $F)" 

cede

/etc/passwd 

Sei al sicuro se ci si rivolge una directory, basename torneranno nulla e ti basta finire con doppie barre in l'output finale.

+1

just readlink -e ../../../../etc/passwd funziona qui –

+0

Ottima voce con 'dirname',' readlink' e 'basename'. Questo mi ha aiutato a ottenere il percorso assoluto di un collegamento simbolico, non il suo obiettivo – kevinarpe

+0

Non funziona quando si desidera restituire il percorso ai collegamenti simbolici (che ho appena dovuto fare ...) –

5

realpath è probabilmente migliore

Ma ...

La domanda iniziale era molto confuso per cominciare, con un esempio poco legati alla domanda, come indicato.

La risposta selezionata risponde effettivamente all'esempio fornito, e non a tutti la domanda nel titolo. Il primo comando è la risposta (è proprio ? Dubito), e potrei fare altrettanto senza il '/'. E non riesco a vedere cosa sta facendo il secondo comando.

Diverse questioni si mescolano:

  • modifica di un percorso relativo in un assoluto, qualunque essa denota, forse nulla. (In genere, se si emette un comando come touch foo/bar, il percorso foo/bar deve esistere per te, e, eventualmente, essere utilizzato in calcolo, prima che il file è effettivamente creato.)

  • ci possono essere più assoluto percorso che denota lo stesso file (o potenziale file), in particolare a causa di collegamenti simbolici (symlink) sul percorso, ma probabilmente per altri motivi (un dispositivo potrebbe essere montato due volte in sola lettura). Uno può o non può voler risolvere esplicitamente tali collegamenti simbolici.

  • fino alla fine di una catena di collegamenti simbolici a un file o nome non symlink . Questo può o non può fornire un nome di percorso assoluto, a seconda di come è fatto. E uno può, o non può volere risolverlo in un percorso assoluto.

Il comando readlink foo senza opzione dà una risposta solo se la sua tesi foo è un link simbolico, e che la risposta è il valore di tale link simbolico. Nessun altro collegamento è seguito. La risposta potrebbe essere un percorso relativo: qualunque fosse il valore dell'argomento symlink.

Tuttavia, readlink dispone di opzioni (-f -e o -m) che funzionerà per tutti i file, e dare un percorso assoluto (quello senza collegamenti simbolici) per il file in realtà indicata con l'argomento.

Questo funziona correttamente per tutto ciò che non è un collegamento simbolico, sebbene uno possa desiderare di utilizzare un percorso assoluto senza risolvere i collegamenti simbolici intermedi sul percorso. Questo viene fatto dal comando realpath -s foo

Nel caso di un argomento collegamento simbolico, readlink con le sue opzioni saranno ancora risolvere tutti i collegamenti simbolici sul percorso assoluto per l'argomento, ma che includerà anche tutti i collegamenti simbolici che si possono incontrare per seguendo il valore dell'argomento. Potresti non volerlo se desideri un percorso assoluto all'argomento symlink stesso, piuttosto che a qualsiasi a cui possa collegarsi.Anche in questo caso, se foo è un collegamento simbolico, otterrà un percorso assoluto senza risolvere i collegamenti simbolici, incluso quello fornito come argomento.

Senza l'opzione -s, realpath fa più o meno lo stesso di readlink, tranne che per la semplice lettura del valore di un collegamento, così come molti altre cose. Non mi è chiaro il motivo per cui readlink ha le sue opzioni , creando apparentemente una ridondanza indesiderata con realpath.

L'esplorazione del web non dice molto di più, tranne che potrebbero esserci variazioni tra i vari sistemi.

Conclusione: realpath è il comando migliore da utilizzare, con la massima flessibilità , almeno per l'uso richiesto qui.

17

penso che questo è il più portatile:

abspath() {            
    cd "$(dirname "$1")" 
    printf "%s/%s\n" "$(pwd)" "$(basename "$1")" 
    cd "$OLDPWD" 
} 

fallirà se il percorso non esiste però.

+2

Non c'è bisogno di tornare indietro Vedi http://stackoverflow.com/a/21188136/1504556. La tua è la migliore risposta in questa pagina, IMHO Per chi è interessato il link fornisce una spiegazione su * perché * questa soluzione funziona. – peterh

+0

Questo non è molto portatile , 'dirname' è un'utilità di base GNU, non comune a tutti i tipi di unixen . – einpoklum

+0

@einpoklum' dirname' è un'utilità standard POSIX, vedere qui: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/dirname .html –

2

Se si utilizza bash su Mac OS X, che non ha realpath esisteva né il suo readlink possibile stampare il percorso assoluto, potrebbe essere scelta, ma per codificare la propria versione per stamparlo. Qui è la mia realizzazione:

(bash pura)

abspath(){ 
    local thePath 
    if [[ ! "$1" =~ ^/ ]];then 
    thePath="$PWD/$1" 
    else 
    thePath="$1" 
    fi 
    echo "$thePath"|(
    IFS=/ 
    read -a parr 
    declare -a outp 
    for i in "${parr[@]}";do 
    case "$i" in 
    ''|.) continue ;; 
    ..) 
     len=${#outp[@]} 
     if ((len==0));then 
     continue 
     else 
     unset outp[$((len-1))] 
     fi 
     ;; 
    *) 
     len=${#outp[@]} 
     outp[$len]="$i" 
     ;; 
    esac 
    done 
    echo /"${outp[*]}" 
) 
} 

(uso gawk)

abspath_gawk() { 
    if [[ -n "$1" ]];then 
     echo $1|gawk '{ 
      if(substr($0,1,1) != "/"){ 
       path = ENVIRON["PWD"]"/"$0 
      } else path = $0 
      split(path, a, "/") 
      n = asorti(a, b,"@ind_num_asc") 
      for(i in a){ 
       if(a[i]=="" || a[i]=="."){ 
        delete a[i] 
       } 
      } 
      n = asorti(a, b, "@ind_num_asc") 
      m = 0 
      while(m!=n){ 
       m = n 
       for(i=1;i<=n;i++){ 
        if(a[b[i]]==".."){ 
         if(b[i-1] in a){ 
          delete a[b[i-1]] 
          delete a[b[i]] 
          n = asorti(a, b, "@ind_num_asc") 
          break 
         } else exit 1 
        } 
       } 
      } 
      n = asorti(a, b, "@ind_num_asc") 
      if(n==0){ 
       printf "/" 
      } else { 
       for(i=1;i<=n;i++){ 
        printf "/"a[b[i]] 
       } 
      } 
     }' 
    fi 
} 

(BSD awk puro)

#!/usr/bin/env awk -f 
function abspath(path, i,j,n,a,b,back,out){ 
    if(substr(path,1,1) != "/"){ 
    path = ENVIRON["PWD"]"/"path 
    } 
    split(path, a, "/") 
    n = length(a) 
    for(i=1;i<=n;i++){ 
    if(a[i]==""||a[i]=="."){ 
     continue 
    } 
    a[++j]=a[i] 
    } 
    for(i=j+1;i<=n;i++){ 
    delete a[i] 
    } 
    j=0 
    for(i=length(a);i>=1;i--){ 
    if(back==0){ 
     if(a[i]==".."){ 
     back++ 
     continue 
     } else { 
     b[++j]=a[i] 
     } 
    } else { 
     if(a[i]==".."){ 
     back++ 
     continue 
     } else { 
     back-- 
     continue 
     } 
    } 
    } 
    if(length(b)==0){ 
    return "/" 
    } else { 
    for(i=length(b);i>=1;i--){ 
     out=out"/"b[i] 
    } 
    return out 
    } 
} 

BEGIN{ 
    if(ARGC>1){ 
    for(k=1;k<ARGC;k++){ 
     print abspath(ARGV[k]) 
    } 
    exit 
    } 
} 
{ 
    print abspath($0) 
} 

esempio:

$ abspath I/am/.//..//the/./god/../of///.././war 
/Users/leon/I/the/war 
0

Simile alla risposta di @ Ernest-a, ma senza influire $OLDPWD o definire una nuova funzione si poteva sparare un subshell (cd <path>; pwd)

$ pwd 
/etc/apache2 
$ cd ../cups 
$ cd - 
/etc/apache2 
$ (cd ~/..; pwd) 
/Users 
$ cd - 
/etc/cups 
24
#! /bin/sh 
echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")" 

UPD Alcune spiegazioni

  1. Questo scri pt ottenere percorso relativo come argomento "$1"
  2. allora otteniamo nomedir parte di quel percorso (è possibile passare sia dir o un file a questo script): dirname "$1"
  3. Poi abbiamo cd "$(dirname "$1") in questa relativa dir e ottenere il percorso assoluto per esso eseguendo il comando pwd shell
  4. Dopo che aggiungiamo basename al percorso assoluto: $(basename "$1")
  5. Come fase finale che si echo
+3

Questa risposta utilizza il miglior idioma di Bash – Rondo

+0

readlink è la soluzione semplice per linux, ma questa soluzione funziona anche su OSX, quindi +1 – thetoolman

+0

Questo è il * reale * risposta. –

0

Se il percorso relativo è un percorso di directory, quindi provare il mio, dovrebbe essere il migliore:

absPath=$(pushd ../SOME_RELATIVE_PATH_TO_Directory > /dev/null && pwd && popd > /dev/null) 

echo $absPath 
+0

Questa soluzione funziona solo per bash, vedi anche http://stackoverflow.com/a/5193087/712014 – Michael

0
echo "mydir/doc/ mydir/usoe ./mydir/usm" | awk '{ split($0,array," "); for(i in array){ system("cd "array[i]" && echo $PWD") } }' 
+3

Grazie per questo snippet di codice, che potrebbe fornire una guida limitata a breve termine. Una spiegazione appropriata [migliorerebbe notevolmente] (// meta.stackexchange.com/q/114762) il suo valore a lungo termine mostrando * perché * questa è una buona soluzione al problema e lo renderebbe più utile ai futuri lettori con altre domande simili. Per favore [modifica] la tua risposta per aggiungere qualche spiegazione, incluse le ipotesi che hai fatto. –

0

Se si vuole trasformare una variabile che contiene un percorso relativo in un assoluto, questo funziona:

dir=`cd "$dir"` 

echo "cd" senza cambiare la directory di lavoro, perché eseguito qui in una sub-shell.

+0

Su bash-4.3-p46, questo non funziona: la shell stampa una riga vuota quando eseguo 'dir = \ cd" .. "\" && echo $ dir' – Michael

3

di risposta Eugen non ha funzionato per me, ma questo ha fatto:

absolute="$(cd $(dirname $file); pwd)/$(basename $file)" 

Nota a margine, la directory di lavoro corrente è influenzato.

+0

Avviso: questo è lo script. dove $ 1 è il primo argomento per questo –