Come recuperare il percorso assoluto di un file arbitrario da OS X.


18

Sto cercando un semplice comando che può essere utilizzato all'interno di Bash per trovare il percorso assoluto e canonicalizzato di un file su un OS X (simile a `` readlink -f '' sotto Linux).

La seguente sessione bash di esempio descrive un'utilità [fittizia] chiamata `` abspath '' che mostra il comportamento desiderato:

$ pwd
/Users/guyfleegman

$ ls -lR
drwxr-xr-x  4 guyfleegman  crew  136 Oct 30 02:09 foo

./foo:
-rw-r--r--  1 guyfleegman  crew  0 Oct 30 02:07 bar.txt
lrwxr-xr-x  1 guyfleegman  crew  7 Oct 30 02:09 baz.txt -> bar.txt


$ abspath .
/Users/guyfleegman

$ abspath foo
/Users/guyfleegman/foo

$ abspath ./foo/bar.txt
/Users/guyfleegman/foo/bar.txt

$ abspath foo/baz.txt
/Users/guyfleegman/foo/baz.txt

Come con l'ultima invocazione di `` abspath '' nell'esempio sopra, preferirei che non risolvesse automaticamente i collegamenti simbolici, ma non sarò troppo esigente qui.


Risposte:


16
function abspath() { pushd . > /dev/null; if [ -d "$1" ]; then cd "$1"; dirs -l +0; else cd "`dirname \"$1\"`"; cur_dir=`dirs -l +0`; if [ "$cur_dir" == "/" ]; then echo "$cur_dir`basename \"$1\"`"; else echo "$cur_dir/`basename \"$1\"`"; fi; fi; popd > /dev/null; }

Esempi:

abspath / => /

abspath /.DS_Store => /.DS_Store

abspath ~ => /Users/mschrag

cd /tmp; abspath . => /tmp

cd /; abspath .DS_Store => /.DS_Store

11

Non penso che ci sia un comando buildin che lo fa. Jesse Wilson ha scritto una sceneggiatura bash per questo:

#!/bin/bash
cd -P -- "$(dirname -- "$1")" &&
printf '%s\n' "$(pwd -P)/$(basename -- "$1")"

Tuttavia, non funziona bene per i percorsi direttamente sotto /, come /etc(stampa //etc), nonché .e ..(stampa /cwd/.in entrambi i casi). Ho provato a modificarlo, ma il mio bash-fu insufficiente mi ha fallito.

Ecco il mio suggerimento:

#!/usr/bin/env python
import os.path
import sys

for arg in sys.argv[1:]:
    print os.path.abspath(arg)

Salva come /usr/bin/abspatho qualcosa del genere e rendilo eseguibile. Uscita campione:

Servus08:~ danielbeck$ abspath .
/Users/danielbeck
Servus08:~ danielbeck$ abspath /tmp
/tmp
Servus08:~ danielbeck$ abspath Documents
/Users/danielbeck/Documents
Servus08:~ danielbeck$ abspath . /tmp Documents
/Users/danielbeck
/tmp
/Users/danielbeck/Documents

Se fai desiderare risoluzione link simbolico, modificare la printriga come questa:

    print os.path.realpath(os.path.abspath(arg))

per ottenere questo:

Servus08:~ danielbeck$ abspath . /tmp Documents
/Users/danielbeck
/private/tmp
/Users/danielbeck/Documents

1
Penso che tu sia sulla buona strada con il primo approccio basato su shell, ma credo che l'uso di un altro linguaggio per fare questo in qualche modo sconfigge lo scopo, in quanto introduce dipendenze aggiuntive ed è praticamente equivalente alla semplice compilazione della versione di GNU di `readlink (1) "sotto OS X (supponendo che sia possibile farlo; non l'ho ancora verificato).
Michael Wehner,

2
@Michael Sei su un sistema con GNU readlink o su OS X - giusto? OS X ha Python pronto all'uso. In teoria è una nuova dipendenza, in pratica no. Ad ogni modo, è tutto ciò che posso offrirti.
Daniel Beck

L'approccio pragmatico che stai insinuando è lodevole, se eccessivamente semplicistico. In ogni caso, se dovessimo adottare questo approccio al posto di qualsiasi cosa scritta esclusivamente in bash (il che è assolutamente fattibile e non dipende da nient'altro - semplicemente non può essere fatto facilmente in una riga), Python probabilmente non lo è è la scelta migliore. Sia Perl che Ruby (e probabilmente PHP) possono farlo in modo succinto sulla riga di comando senza la necessità di creare un vero file di script.
Michael Wehner,

1
@Michael: Vero, ma non è quello che stavo commentando. Ti offro una soluzione al 90% scritta in puro bash da Jesse Wilson + l'analisi del perché è solo il 90%. Se questo non è un problema per te, va bene. Ti ho anche dato una soluzione al 100% leggermente più complicata in Python. Mentre altri linguaggi di scripting potrebbero essere più brevi (Perl così famigeratamente :-)), tutti richiedono un file aggiuntivo per memorizzare il comando. O vuoi scriverlo ogni volta che vuoi usarlo? Questo è anche il motivo per cui ho aggiunto la capacità multi-linea di gestire più parametri, 1 o 2 linee, quindi non fare diversamente.
Daniel Beck

2
@Michael, perché Python non dovrebbe essere una buona scelta su OS X? Viene anche fornito con comandi che sono in realtà script Python, comefile /usr/bin/xattr
Arjan,

8

Un'opzione sarebbe semplicemente installare coreutils e usarli greadlink -f. Risolve i collegamenti simbolici e funziona con /Foo/o ~/foo.txtse non esistono, ma non con /Foo/foo.txtse /Foo/non esiste.

$ brew install coreutils
$ greadlink -f /etc
/private/etc
$ greadlink -f ~/Documents/
/Users/lauri/Documents
$ greadlink -f ..
/Users
$ greadlink -f //etc/..////
/private
$ greadlink -f /Foo
/Foo
$ greadlink -f /Foo/foo.txt
$ 

Questo non risolve i collegamenti simbolici e non funziona con /Foo/foo.txtnessuno dei due.

abspath() {
  if [ -d "$1" ]; then
    ( cd "$1"; dirs -l +0 )
  else
    ( cd "$(dirname "$1")"; d=$(dirs -l +0); echo "${d%/}/${1##*/}" )
  fi
}

abspath /etc # /etc
abspath ~/Foo/foo.txt # doesn't work
abspath ~/Foo # works
abspath .
abspath ./
abspath ../
abspath ..
abspath /
abspath ~
abspath ~/
abspath ~/Documents
abspath /\"\ \'
abspath /etc/../etc/
abspath /private//etc/
abspath /private//
abspath //private # //private
abspath ./aa.txt
abspath aa.tar.gz
abspath .aa.txt
abspath /.DS_Store
abspath ~/Documents/Books/

dirs -lesegue l'espansione della tilde. dirs +0stampa solo la directory più in alto se ci sono altre directory nello stack.


2

Immagino che potresti farlo con il pitone o il rubino.

$ ruby -e 'puts File.expand_path("~/somepath")'

o renderlo un comando con

#!/usr/bin/env ruby
puts File.expand_path(ARGV[0])

Il mio precedente commento sulla risposta di Daniel Beck si applica anche qui (preferirei una soluzione puramente Bash-y), anche se se dovessi ricorrere a un'altra lingua per raggiungere questo obiettivo, mi piace la tua soluzione finora per la sua brevità. :) Avrei probabilmente incluso anche la chiamata a ruby ​​(es. Funzione abspath () {ruby -e "inserisce File.expand_path ('$ 1')";}) e lo inserisco nel mio `.profile '.
Michael Wehner,

1

Se hai installato il modulo File :: Spec per perl puoi semplicemente fare questo:

perl -MFile::Spec -e 'print File::Spec->rel2abs("../however/complex/../you/want/to.get"), "\n"'

0

Per gli script bash / sh è possibile utilizzare questa funzione ricorsiva:

canonical_readlink ()
{
    cd `dirname $1`;
    __filename=`basename $1`;
    if [ -h "$__filename" ]; then
        canonical_readlink `readlink $__filename`;
    else
        echo "`pwd -P`";
    fi
}

answer=$(canonical_readlink $0)

Alcune variabili e sostituzioni di comandi non sono quotate correttamente. È possibile utilizzare variabili locali anziché nomi di variabili come __filename. Lo script attualmente si comporta più come in dirnameogni caso.
Lri,

0

Installa la seguente libreria per OSX:

brew install coreutils

greadlink -f file.txt

0

Se l'installazione di coreutils non è un'opzione, i seguenti gestiscono combinazioni di collegamenti simbolici,. e .. e funziona su file e cartelle come GNU realpath fa:

#!/usr/bin/env bash
realpath()
{
    if ! pushd $1 &> /dev/null; then 
        pushd ${1##*/} &> /dev/null
        echo $( pwd -P )/${1%/*}
    else
        pwd -P
    fi
    popd > /dev/null
}

Ma non supporta realpath's --relative-to. Ciò richiederebbe /programming//a/12498485/869951 .


0

Dato che il vincolo è MacOS (OS X al momento), PHP è disponibile per impostazione predefinita. Ciò restituirà la directory principale del file. Rimuovi dirnameper ottenere anche il file.

export SOURCE_DIRECTORY="$(php -r 'echo dirname(realpath($argv[1]));' -- "${BASH_SOURCE[0]}")"
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.