Come si normalizza un percorso di file in Bash?


204

Voglio trasformarmi /foo/bar/.. in/foo

C'è un comando bash che lo fa?


Modifica: nel mio caso pratico, la directory esiste.


4
Importa se esiste, /foo/baro addirittura /fooesiste, o sei interessato solo all'aspetto della manipolazione delle stringhe secondo le regole del nome del percorso?
Chen Levy,

5
@twalberg ... è un po 'inventato ...
Camilo Martin,

2
@CamiloMartin Non riuscì a tutti - fa esattamente ciò che la domanda chiede - trasforma /foo/bar/..a /foo, e utilizzando un bashcomando. Se ci sono altri requisiti che non sono stati dichiarati, forse dovrebbero essere ...
twalberg,

14
@twalberg Hai fatto troppo TDD -_- '
Camilo Martin il

Risposte:


193

se vuoi separare parte del nome di un file dal percorso, "dirname" e "basename" sono i tuoi amici e anche "realpath" è utile.

dirname /foo/bar/baz 
# /foo/bar 
basename /foo/bar/baz
# baz
dirname $( dirname  /foo/bar/baz  ) 
# /foo 
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp

realpath alternative

Se realpathnon è supportato dalla tua shell, puoi provare

readlink -f /path/here/.. 

Anche

readlink -m /path/there/../../ 

Funziona come

realpath -s /path/here/../../

in quanto non è necessario che il percorso esista per essere normalizzato.


5
Per quelli di voi che necessitano di una soluzione OS X, date un'occhiata alla risposta di Adam Liss di seguito.
Trenton,

stackoverflow.com/a/17744637/999943 Questa è una risposta fortemente correlata! Mi sono imbattuto in entrambi questi post QA in un giorno e volevo collegarli insieme.
Phyatt,

realpath sembra essere stato aggiunto a coreutils nel 2012. Vedi la cronologia dei file su github.com/coreutils/coreutils/commits/master/src/realpath.c .
Noah Lavine,

1
Entrambi realpathe readlinkprovengono dalle utility GNU core, quindi molto probabilmente ottieni entrambi o nessuno. Se ricordo bene, la versione readlink su Mac è leggermente diversa da quella GNU: - \
Antoine 'hashar' Musso

100

Non so se esiste un comando bash diretto per farlo, ma di solito lo faccio

normalDir="`cd "${dirToNormalize}";pwd`"
echo "${normalDir}"

e funziona bene.


9
Ciò normalizzerà ma non risolverà i collegamenti software. Potrebbe trattarsi di un bug o di una funzione. :-)
Adam Liss,

4
Ha anche un problema se si definisce $ CDPATH; perché "cd foo" passerà a qualsiasi directory "foo" che è una sottodirectory di $ CDPATH, non solo un "foo" che si trova nella directory corrente. Penso che devi fare qualcosa del tipo: CDPATH = "" cd "$ {dirToNormalize}" && pwd -P.
mjs,

7
La risposta di Tim è sicuramente la più semplice e portatile. CDPATH è facile da gestire: dir = "$ (disinserito CDPATH && cd" $ dir "&& pwd)"
David Blevins

2
Questo potrebbe essere molto pericoloso ( rm -rf $normalDir) se dirToNormalize non esiste!
Frank Meulenaar,

4
Sì, probabilmente è meglio usare un &&secondo il commento di @ DavidBlevins.
Elias Dorneles,

54

Prova realpath. Di seguito la fonte nella sua interezza, con la presente donata al pubblico dominio.

// realpath.c: display the absolute path to a file or directory.
// Adam Liss, August, 2007
// This program is provided "as-is" to the public domain, without express or
// implied warranty, for any non-profit use, provided this notice is maintained.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>   
#include <limits.h>

static char *s_pMyName;
void usage(void);

int main(int argc, char *argv[])
{
    char
        sPath[PATH_MAX];


    s_pMyName = strdup(basename(argv[0]));

    if (argc < 2)
        usage();

    printf("%s\n", realpath(argv[1], sPath));
    return 0;
}    

void usage(void)
{
    fprintf(stderr, "usage: %s PATH\n", s_pMyName);
    exit(1);
}

1
Questo è incluso come parte dell'installazione bash standard? Ricevo "comando non trovato" sul nostro sistema (bash 3.00.15 (1) su RHEL)
Tim Whitcomb,

4
gnu.org/software/coreutils per readlink, ma realpath proviene da questo pacchetto: packages.debian.org/unstable/utils/realpath
Kent Fredric

8
È standard su Ubuntu / BSD, non su Centos / OSX
Erik Aronesty

4
Dovresti davvero colpire la parte con "Bonus: è un comando bash, e disponibile ovunque!" parte circa realpath. Capita anche di essere il mio preferito, ma invece di complimentare il tuo strumento dalla fonte. È tutto per i pesi massimi, e la maggior parte del tempo che vuoi solo readlink -f... A proposito, readlinkNON è anche un bash incorporato, ma parte di coreutilsUbuntu.
Tomasz Gandor,

4
Hai detto che lo stai donando al pubblico dominio, ma l'intestazione dice anche "per qualsiasi uso senza scopo di lucro" - intendi davvero donarlo al pubblico dominio? Ciò significherebbe che può essere utilizzato per scopi commerciali a scopo di lucro e anche senza scopo di lucro ...
me_e

36

Una soluzione portatile e affidabile è l'uso di Python, che è preinstallato praticamente ovunque (incluso Darwin). Hai due opzioni:

  1. abspath restituisce un percorso assoluto ma non risolve i collegamenti simbolici:

    python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file

  2. realpath restituisce un percorso assoluto e in tal modo risolve i collegamenti simbolici, generando un percorso canonico:

    python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file

In ogni caso, path/to/filepuò essere un percorso relativo o assoluto.


7
Grazie, è stato l'unico che ha funzionato. readlink o realpath non sono disponibili in OS X. Python dovrebbe trovarsi sulla maggior parte delle piattaforme.
sorin,

1
Giusto per chiarire, readlinkè disponibile su OS X, ma non con l' -fopzione. Soluzioni alternative portatili discusse qui .
StvnW

3
Mi fa letteralmente impazzire che questa è l'unica soluzione sana se non vuoi seguire i link. Il modo Unix il mio PIEDE.
DannoHung,

34

Utilizzare l'utilità readlink dal pacchetto coreutils.

MY_PATH=$(readlink -f "$0")

1
BSD non ha il flag -f, il che significa che fallirà anche sull'ultimo MacOS Mojave e su molti altri sistemi. Dimentica di usare -f se vuoi la portabilità, molti sistemi operativi sono interessati.
Sorin,

1
@sorin. La domanda non riguarda Mac, ma Linux.
mattalxndr,

14

readlinkè lo standard bash per ottenere il percorso assoluto. Ha anche il vantaggio di restituire stringhe vuote se non esistono percorsi o percorsi (dati i flag per farlo).

Per ottenere il percorso assoluto di una directory che può esistere o meno, ma che esistono i genitori, utilizzare:

abspath=$(readlink -f $path)

Per ottenere il percorso assoluto di una directory che deve esistere insieme a tutti i genitori:

abspath=$(readlink -e $path)

Per canonicalizzare il percorso specificato e seguire i collegamenti simbolici se esistono, ma altrimenti ignorare le directory mancanti e restituire comunque il percorso, è:

abspath=$(readlink -m $path)

L'unico aspetto negativo è che readlink seguirà i collegamenti. Se non si desidera seguire i collegamenti, è possibile utilizzare questa convenzione alternativa:

abspath=$(cd ${path%/*} && echo $PWD/${path##*/})

Verrà indirizzato alla parte della directory di $ path e stamperà la directory corrente insieme alla parte del file di $ path. Se non riesce a chdir, si ottiene una stringa vuota e un errore su stderr.


7
readlinkè una buona opzione se è disponibile. La versione di OS X non supporta le opzioni -eo -f. Nei primi tre esempi, dovresti avere doppie virgolette intorno $pathper gestire spazi o caratteri jolly nel nome del file. +1 per l'espansione dei parametri, ma questo ha una vulnerabilità di sicurezza. Se pathè vuoto, questo sarà cdnella tua home directory. Hai bisogno di virgolette doppie. abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
Toxalot,

Questo era solo un esempio. Se sei molto interessato alla sicurezza, allora non dovresti assolutamente usare bash o qualsiasi altra variante della shell. Inoltre, bash ha i suoi problemi quando si tratta di compatibilità multipiattaforma, oltre ad avere problemi con il cambio di funzionalità tra le versioni principali. OSX è solo una delle tante piattaforme con problemi relativi allo scripting della shell, per non parlare del fatto che si basa su BSD. Quando devi essere veramente multipiattaforma, devi essere conforme a POSIX, quindi l'espansione dei parametri esce davvero dalla finestra. Dai un'occhiata a Solaris o HP-UX qualche volta.
Craig,

6
Non significa nessuna offesa qui, ma evidenziare questioni oscure come questa è importante. Voglio solo una rapida risposta a questo banale problema e mi sarei fidato di quel codice con qualsiasi / tutti gli input se non fosse stato per il commento sopra. È anche importante supportare OS-X in queste discussioni bash. Ci sono molti comandi che purtroppo non sono supportati su OS-X, e molti forum lo danno per scontato quando si discute di Bash, il che significa che continueremo a ricevere molti problemi multipiattaforma a meno che non vengano trattati prima o poi.
Rebs,

13

Vecchia domanda, ma c'è un modo molto più semplice se hai a che fare con nomi di percorsi completi a livello di shell:

   abspath = "$ (cd" $ path "&& pwd)"

Poiché il cd si presenta in una subshell non ha alcun impatto sullo script principale.

Due varianti, supponendo che i comandi incorporati della shell accettano -L e -P, sono:

   abspath = "$ (cd -P" $ percorso "&& pwd -P)" # percorso fisico con collegamenti simbolici risolti
   abspath = "$ (cd -L" $ path "&& pwd -L)" #percorso logico che preserva i collegamenti simbolici

Personalmente, raramente ho bisogno di questo approccio successivo a meno che non sia affascinato dai collegamenti simbolici per qualche motivo.

Cordiali saluti: variazione su come ottenere la directory iniziale di uno script che funziona anche se lo script cambia la directory corrente in un secondo momento.

name0 = "$ (basename" $ ​​0 ")"; #base name of script
dir0 = "$ (cd" $ (dirname "$ 0") "&& pwd)"; #absolute dir iniziale

L'uso del CD ti assicura di avere sempre la directory assoluta, anche se lo script è eseguito da comandi come ./script.sh che, senza cd / pwd, spesso dà solo .. Inutile se lo script fa un cd in seguito.


8

Come ha osservato Adam Liss, realpathnon è incluso in ogni distribuzione. È un peccato, perché è la soluzione migliore. Il codice sorgente fornito è eccezionale e probabilmente inizierò ad usarlo ora. Ecco quello che ho usato fino ad ora, che condivido qui solo per completezza:

get_abs_path() {
     local PARENT_DIR=$(dirname "$1")
     cd "$PARENT_DIR"
     local ABS_PATH="$(pwd)"/"$(basename "$1")"
     cd - >/dev/null
     echo "$ABS_PATH"
} 

Se vuoi che risolva i collegamenti simbolici, sostituiscili pwdcon pwd -P.


Un gotcha con l' pwd -Popzione per questo caso ... Considera cosa accadrebbe se $(basename "$1")fosse un collegamento simbolico a un file in un'altra directory. L' pwd -Punico risolve i collegamenti simbolici nella parte della directory del percorso, ma non nella parte del nome di base.
Toxalot,

7

La mia recente soluzione è stata:

pushd foo/bar/..
dir=`pwd`
popd

Basato sulla risposta di Tim Whitcomb.


Sospetto che fallisca se l'argomento non è una directory. Supponiamo di voler sapere dove porta / usr / bin / java?
Edward Falk,

1
Se sai che è un file, puoi pushd $(dirname /usr/bin/java)provare.
schmunk,

5

Non esattamente una risposta ma forse una domanda di follow-up (la domanda originale non era esplicita):

readlinkva bene se vuoi davvero seguire i symlink. Ma v'è anche un caso d'uso per normalizzare meramente ./ed ../e //sequenze, che può essere fatto puramente sintatticamente, senza canonicalizing link simbolici. readlinknon va bene per questo, e nemmeno lo è realpath.

for f in $paths; do (cd $f; pwd); done

funziona per percorsi esistenti, ma si interrompe per gli altri.

Una sedsceneggiatura sembrerebbe essere una buona scommessa, se non che non è possibile sostituire iterativamente sequenze ( /foo/bar/baz/../..-> /foo/bar/..-> /foo) senza usare qualcosa di simile a Perl, che non è lecito ritenere su tutti i sistemi, o usando qualche brutto ciclo per confrontare l'output di seda il suo input.

FWIW, one-liner che utilizza Java (JDK 6+):

jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths

realpathha -sun'opzione di non risolvere i collegamenti simbolici e solo riferimenti determinazione a /./, /../e rimuovere in più /caratteri. Se combinato con l' -mopzione, realpathopera solo sul nome del file e non tocca alcun file effettivo. E suona come la soluzione perfetta. Ma ahimè, realpathmanca ancora su molti sistemi.
Toxalot,

La rimozione dei ..componenti non può essere eseguita sintatticamente quando sono coinvolti collegamenti simbolici. /one/two/../threenon è lo stesso di /one/threese twoè un link simbolico a /foo/bar.
jrw32982 supporta Monica il

@ jrw32982 sì, come ho detto nella mia risposta, questo è per un caso d'uso in cui la canonicalizzazione symlink non è richiesta o necessaria.
Jesse Glick,

@JesseGlick non è solo il caso di voler canonicalizzare i link simbolici. Il tuo algoritmo in realtà produce la risposta sbagliata. Perché la tua risposta sia corretta, dovresti sapere a priori che non c'erano collegamenti simbolici (o che erano solo di una certa forma). La tua risposta dice che non vuoi canonicalizzarli, non che non ci siano collegamenti simbolici coinvolti nel percorso.
jrw32982 supporta Monica l'

Ci sono casi d'uso in cui la normalizzazione deve essere eseguita senza assumere alcuna struttura di directory fissa ed esistente. La normalizzazione dell'URI è simile. In questi casi è una limitazione intrinseca che il risultato non sarà generalmente corretto se si verificano collegamenti simbolici vicino a una directory in cui il risultato verrà successivamente applicato.
Jesse Glick,

5

Sono in ritardo alla festa, ma questa è la soluzione che ho creato dopo aver letto un sacco di discussioni come questa:

resolve_dir() {
        (builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"` 2>/dev/null; if [ $? -eq 0 ]; then pwd; fi)
}

Questo risolverà il percorso assoluto di $ 1, giocherà bene con ~, manterrà i collegamenti simbolici nel percorso in cui si trovano e non rovinerà il tuo stack di directory. Restituisce il percorso completo o nulla se non esiste. Si aspetta che $ 1 sia una directory e probabilmente fallirà se non lo è, ma è un controllo facile da fare da soli.


4

Loquace, e un po 'in ritardo risposta. Devo scriverne uno poiché sono bloccato su RHEL4 / 5 precedente. Gestisco collegamenti assoluti e relativi e semplifica le voci //, /./ e somedir /../.

test -x /usr/bin/readlink || readlink () {
        echo $(/bin/ls -l $1 | /bin/cut -d'>' -f 2)
    }


test -x /usr/bin/realpath || realpath () {
    local PATH=/bin:/usr/bin
    local inputpath=$1
    local changemade=1
    while [ $changemade -ne 0 ]
    do
        changemade=0
        local realpath=""
        local token=
        for token in ${inputpath//\// }
        do 
            case $token in
            ""|".") # noop
                ;;
            "..") # up one directory
                changemade=1
                realpath=$(dirname $realpath)
                ;;
            *)
                if [ -h $realpath/$token ] 
                then
                    changemade=1
                    target=`readlink $realpath/$token`
                    if [ "${target:0:1}" = '/' ]
                    then
                        realpath=$target
                    else
                        realpath="$realpath/$target"
                    fi
                else
                    realpath="$realpath/$token"
                fi
                ;;
            esac
        done
        inputpath=$realpath
    done
    echo $realpath
}

mkdir -p /tmp/bar
(cd /tmp ; ln -s /tmp/bar foo; ln -s ../.././usr /tmp/bar/link2usr)
echo `realpath /tmp/foo`

3

Prova il nostro nuovo prodotto della libreria Bash realpath-lib che abbiamo inserito su GitHub per un uso gratuito e senza restrizioni. È accuratamente documentato e costituisce un ottimo strumento di apprendimento.

Risolve percorsi locali, relativi e assoluti e non ha dipendenze tranne Bash 4+; quindi dovrebbe funzionare praticamente ovunque. È gratuito, pulito, semplice ed istruttivo.

Tu puoi fare:

get_realpath <absolute|relative|symlink|local file path>

Questa funzione è il nucleo della libreria:

function get_realpath() {

if [[ -f "$1" ]]
then 
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then 
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else 
        # file *must* be local
        local tmppwd="$PWD"
    fi
else 
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

Contiene anche funzioni per get_dirname, get_filename, get_ stemname e validate_path. Provalo su tutte le piattaforme e aiuta a migliorarlo.


2

Il problema realpathè che non è disponibile su BSD (o OSX per quella materia). Ecco una semplice ricetta estratta da un articolo piuttosto vecchio (2009) del Linux Journal , che è abbastanza portatile:

function normpath() {
  # Remove all /./ sequences.
  local path=${1//\/.\//\/}

  # Remove dir/.. sequences.
  while [[ $path =~ ([^/][^/]*/\.\./) ]]; do
    path=${path/${BASH_REMATCH[0]}/}
  done
  echo $path
}

Si noti che questa variante non richiede che il percorso esista.


Ciò non risolve tuttavia i collegamenti simbolici. Realpath elabora i percorsi iniziando dalla radice e segue i collegamenti simbolici man mano che procede. Tutto ciò che fa è comprimere i riferimenti principali.
Martijn Pieters

2

Sulla base della risposta di @ Andre, potrei avere una versione leggermente migliore, nel caso qualcuno stia cercando una soluzione completamente priva di loop, basata sulla manipolazione delle stringhe. È anche utile per coloro che non vogliono sottovalutare alcun collegamento simbolico, che è il rovescio della medaglia dell'uso di realpatho readlink -f.

Funziona su bash versioni 3.2.25 e successive.

shopt -s extglob

normalise_path() {
    local path="$1"
    # get rid of /../ example: /one/../two to /two
    path="${path//\/*([!\/])\/\.\./}"
    # get rid of /./ and //* example: /one/.///two to /one/two
    path="${path//@(\/\.\/|\/+(\/))//}"
    # remove the last '/.'
    echo "${path%%/.}"
}

$ normalise_path /home/codemedic/../codemedic////.config
/home/codemedic/.config

Questa è una bella idea, ma ho perso 20 minuti cercando di farlo funzionare su diverse versioni di bash. Si scopre che l'opzione shell extglob deve essere attiva perché funzioni, e non è di default. Quando si tratta della funzionalità bash, è importante specificare sia la versione richiesta che le opzioni non predefinite poiché questi dettagli possono variare tra i sistemi operativi. Ad esempio, una versione recente di Mac OSX (Yosemite) viene fornita solo con una versione obsoleta di bash (3.2).
drwatsoncode

Siamo spiacenti @ricovox; Ora ho aggiornato quelli. Sono ansioso di conoscere la versione esatta di Bash che hai lì. La formula sopra (aggiornata) funziona su CentOS 5.8 che viene fornito con bash 3.2.25
ϹοδεMεδιϲ

Dispiace per la confusione. Questo codice ha funzionato sulla mia versione di Mac OSX bash (3.2.57) dopo aver attivato extglob. La mia nota sulle versioni di bash era più generale (che in realtà si applica di più a un'altra risposta qui riguardo a regex in bash).
drwatsoncode

2
Apprezzo la tua risposta però. L'ho usato come base per il mio. Per inciso ho notato diversi casi in cui il tuo fallisce: (1) Percorsi relativi hello/../world(2) Punti nel nome del file /hello/..world(3) Punti dopo la doppia barra /hello//../world(4) Punto prima o dopo la doppia barra /hello//./worldo /hello/.//world (5) Padre dopo la corrente: /hello/./../world/ (6) Padre dopo Parent:, /hello/../../worldecc. - Alcuni di questi possono essere risolti utilizzando un loop per apportare correzioni fino a quando il percorso non smette di cambiare. (Rimuovi anche dir/../, non /dir/..ma rimuovi dir/..dalla fine.)
drwatsoncode

0

Basato sull'eccellente frammento di pitone di loveborg, ho scritto questo:

#!/bin/sh

# Version of readlink that follows links to the end; good for Mac OS X

for file in "$@"; do
  while [ -h "$file" ]; do
    l=`readlink $file`
    case "$l" in
      /*) file="$l";;
      *) file=`dirname "$file"`/"$l"
    esac
  done
  #echo $file
  python -c "import os,sys; print os.path.abspath(sys.argv[1])" "$file"
done

0
FILEPATH="file.txt"
echo $(realpath $(dirname $FILEPATH))/$(basename $FILEPATH)

Funziona anche se il file non esiste. Richiede l'esistenza della directory contenente il file.


GNU Realpath non richiede neanche l'ultimo elemento nel percorso, a meno che tu non usirealpath -e
Martijn Pieters

0

Avevo bisogno di una soluzione che avrebbe fatto tutte e tre le cose:

  • Lavora su un Mac di serie. realpathereadlink -f sono addon
  • Risolvi i collegamenti simbolici
  • Gestione degli errori

Nessuna delle risposte aveva sia il numero 1 che il numero 2. Ho aggiunto il n. 3 per salvare altri eventuali rasatura di yak.

#!/bin/bash

P="${1?Specify a file path}"

[ -e "$P" ] || { echo "File does not exist: $P"; exit 1; }

while [ -h "$P" ] ; do
    ls="$(ls -ld "$P")"
    link="$(expr "$ls" : '.*-> \(.*\)$')"
    expr "$link" : '/.*' > /dev/null &&
        P="$link" ||
        P="$(dirname "$P")/$link"
done
echo "$(cd "$(dirname "$P")"; pwd)/$(basename "$P")"

Ecco un breve test case con alcuni spazi contorti nei percorsi per esercitare pienamente la quotazione

mkdir -p "/tmp/test/ first path "
mkdir -p "/tmp/test/ second path "
echo "hello" > "/tmp/test/ first path / red .txt "
ln -s "/tmp/test/ first path / red .txt " "/tmp/test/ second path / green .txt "

cd  "/tmp/test/ second path "
fullpath " green .txt "
cat " green .txt "

0

So che questa è una domanda antica. Sto ancora offrendo un'alternativa. Recentemente ho riscontrato lo stesso problema e non ho trovato alcun comando esistente e portatile per farlo. Quindi ho scritto il seguente script di shell che include una funzione che può fare il trucco.

#! /bin/sh                                                                                                                                                

function normalize {
  local rc=0
  local ret

  if [ $# -gt 0 ] ; then
    # invalid
    if [ "x`echo $1 | grep -E '^/\.\.'`" != "x" ] ; then
      echo $1
      return -1
    fi

    # convert to absolute path
    if [ "x`echo $1 | grep -E '^\/'`" == "x" ] ; then
      normalize "`pwd`/$1"
      return $?
    fi

    ret=`echo $1 | sed 's;/\.\($\|/\);/;g' | sed 's;/[^/]*[^/.]\+[^/]*/\.\.\($\|/\);/;g'`
  else
    read line
    normalize "$line"
    return $?
  fi

  if [ "x`echo $ret | grep -E '/\.\.?(/|$)'`" != "x" ] ; then
    ret=`normalize "$ret"`
    rc=$?
  fi

  echo "$ret"
  return $rc
}

https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c


0

Ho realizzato una funzione solo integrata per gestirlo con un focus sulle massime prestazioni possibili (per divertimento). Non risolve i collegamenti simbolici, quindi è sostanzialmente lo stesso di realpath -sm.

## A bash-only mimic of `realpath -sm`. 
## Give it path[s] as argument[s] and it will convert them to clean absolute paths
abspath () { 
  ${*+false} && { >&2 echo $FUNCNAME: missing operand; return 1; };
  local c s p IFS='/';  ## path chunk, absolute path, input path, IFS for splitting paths into chunks
  local -i r=0;         ## return value

  for p in "$@"; do
    case "$p" in        ## Check for leading backslashes, identify relative/absolute path
    '') ((r|=1)); continue;;
    //[!/]*)  >&2 echo "paths =~ ^//[^/]* are impl-defined; not my problem"; ((r|=2)); continue;;
    /*) ;;
    *)  p="$PWD/$p";;   ## Prepend the current directory to form an absolute path
    esac

    s='';
    for c in $p; do     ## Let IFS split the path at '/'s
      case $c in        ### NOTE: IFS is '/'; so no quotes needed here
      ''|.) ;;          ## Skip duplicate '/'s and '/./'s
      ..) s="${s%/*}";; ## Trim the previous addition to the absolute path string
      *)  s+=/$c;;      ### NOTE: No quotes here intentionally. They make no difference, it seems
      esac;
    done;

    echo "${s:-/}";     ## If xpg_echo is set, use `echo -E` or `printf $'%s\n'` instead
  done
  return $r;
}

Nota: questa funzione non gestisce i percorsi che iniziano con //, poiché esattamente due doppie barre all'inizio di un percorso sono comportamenti definiti dall'implementazione. Tuttavia, gestisce /,/// e così via bene.

Questa funzione sembra gestire correttamente tutti i casi limite, ma potrebbero essercene ancora alcuni là fuori che non ho affrontato.

Nota sulle prestazioni: quando viene chiamato con migliaia di argomenti, abspathviene eseguito circa 10 volte più lentamente di realpath -sm; quando viene chiamato con un singolo argomento, abspathviene eseguito> 110 volte più veloce rispetto realpath -smalla mia macchina, principalmente a causa della non necessità di eseguire un nuovo programma ogni volta.


-1

Oggi ho scoperto che puoi usare il statcomando per risolvere i percorsi.

Quindi per una directory come "~ / Documents":

Puoi eseguire questo:

stat -f %N ~/Documents

Per ottenere il percorso completo:

/Users/me/Documents

Per i collegamenti simbolici, puoi utilizzare l'opzione di formato% Y:

stat -f %Y example_symlink

Che potrebbe restituire un risultato come:

/usr/local/sbin/example_symlink

Le opzioni di formattazione potrebbero essere diverse su altre versioni di * NIX, ma queste hanno funzionato per me su OSX.


1
la stat -f %N ~/Documentslinea è una falsa pista ... la shell sta sostituendo ~/Documentscon /Users/me/Documents, ed statè solo la stampa il suo argomento parola per parola.
danwyand,

-4

Una soluzione semplice che utilizza node.js:

#!/usr/bin/env node
process.stdout.write(require('path').resolve(process.argv[2]));
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.