Cómo recuperar la ruta absoluta de un archivo arbitrario del OS X


18

Estoy buscando un comando simple que se pueda usar dentro de Bash para encontrar la ruta absoluta y canonizada a un archivo en un OS X (similar a `` readlink -f '' en Linux).

La siguiente sesión de bash de muestra describe una utilidad [ficticia] llamada `` abspath '' que exhibe el comportamiento deseado:

$ 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

Al igual que con la última invocación de `` abspath '' en el ejemplo anterior, preferiría que no resolviera automáticamente los enlaces simbólicos, pero no voy a ser demasiado exigente aquí.


Respuestas:


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; }

Ejemplos:

abspath / => /

abspath /.DS_Store => /.DS_Store

abspath ~ => /Users/mschrag

cd /tmp; abspath . => /tmp

cd /; abspath .DS_Store => /.DS_Store

11

No creo que haya un comando buildin que haga esto. Jesse Wilson escribió un script bash para esto:

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

Sin embargo, no funciona bien para rutas directamente debajo /, como /etc(impresión //etc), así como .y ..(impresión /cwd/.en ambos casos). Intenté modificarlo, pero mi insuficiente bash-fu me falló.

Aquí está mi sugerencia:

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

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

Guardar como /usr/bin/abspatho algo así y hacerlo ejecutable. Salida de muestra:

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

Si haces querer resolución enlace simbólico, cambiar la printlínea como la siguiente:

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

para obtener esto:

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

1
Creo que está en el camino correcto con el primer enfoque basado en shell, pero creo que usar otro lenguaje para hacer esto de alguna manera frustra el propósito, ya que introduce dependencias adicionales y es más o menos equivalente a simplemente compilar la versión de GNU de `readlink (1) 'bajo OS X (suponiendo que esto se pueda hacer; aún no lo he verificado).
Michael Wehner el

2
@ Michael Estás en un sistema con enlace de lectura GNU o en OS X, ¿verdad? OS X tiene Python fuera de la caja. En teoría es una nueva dependencia, en la práctica no. De todos modos, es todo lo que puedo ofrecerte.
Daniel Beck

El enfoque pragmático que estás dando a entender es loable, aunque demasiado simplista. En cualquier caso, si tuviéramos que adoptar este enfoque en lugar de cualquier cosa escrita exclusivamente en bash (que es absolutamente factible y no depende de nada más, simplemente no se puede hacer fácilmente en una línea), Python probablemente no sea Es la mejor opción. Tanto Perl como Ruby (y probablemente PHP) pueden hacer esto sucintamente en la línea de comando sin la necesidad de crear un archivo de script real.
Michael Wehner el

1
@ Michael: Cierto, pero eso no es lo que estaba comentando. Te ofrezco una solución del 90% escrita en puro bash por Jesse Wilson + el análisis de por qué es solo el 90%. Si eso no es problema para ti, está bien. También te di una solución 100% un poco más complicada en Python. Mientras que otros lenguajes de script pueden ser más breves (Perl infamemente así :-)), todos requieren un archivo adicional para almacenar el comando. ¿O quieres escribirlo cada vez que quieras usarlo? Esa es también la razón por la que agregué la capacidad de varias líneas para manejar múltiples parámetros, 1 o 2 líneas y luego no hacer una diferencia.
Daniel Beck

2
@ Michael, ¿por qué Python no sería una buena opción en OS X? Incluso se envía con comandos que en realidad son scripts de Python, comofile /usr/bin/xattr
Arjan, el

8

Una opción sería simplemente instalar coreutils y usar greadlink -f. Resuelve enlaces simbólicos y funciona con /Foo/o ~/foo.txtsi no existen, pero no con /Foo/foo.txtsi /Foo/no existe.

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

Esto no resuelve los enlaces simbólicos, y tampoco funciona con /Foo/foo.txtninguno.

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 -lrealiza la expansión de tilde. dirs +0imprime solo el directorio superior si hay otros directorios en la pila.


2

Supongo que podrías hacerlo con python o ruby.

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

o convertirlo en un comando con

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

Mi comentario anterior sobre la respuesta de Daniel Beck también se aplica aquí (preferiría una solución puramente Bash-y), aunque si tuviera que recurrir al uso de otro idioma para lograr esto, me gustaría que su solución sea la mejor por su brevedad. :) También probablemente envolvería la llamada a ruby ​​(por ejemplo, la función abspath () {ruby -e "pone File.expand_path ('$ 1')";}) y lo pongo en mi `.profile '.
Michael Wehner el

1

Si tiene instalado el módulo File :: Spec para perl, puede hacer esto:

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

0

Para los scripts bash / sh puedes usar esta función recursiva:

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)

Algunas variables y sustituciones de comandos no se citan correctamente. Puede usar variables locales en lugar de nombres de variables como __filename. El script actualmente se comporta más como de dirnametodos modos.
Lri

0

Instale la siguiente biblioteca para OSX:

brew install coreutils

greadlink -f file.txt

0

Si la instalación de coreutils no es una opción, lo siguiente maneja combinaciones de enlaces simbólicos,. y ... y funciona en archivos y carpetas como lo hace GNU realpath:

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

Pero no admite realpath's --relative-to. Esto requeriría /programming//a/12498485/869951 .


0

Dado que la restricción es MacOS (OS X en ese momento), PHP está disponible por defecto. Esto devolverá el directorio raíz del archivo. Eliminar dirnamepara obtener el archivo también.

export SOURCE_DIRECTORY="$(php -r 'echo dirname(realpath($argv[1]));' -- "${BASH_SOURCE[0]}")"
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.