So rufen Sie den absoluten Pfad einer beliebigen Datei unter OS X ab

18

Ich suche einen einfachen Befehl, der in Bash verwendet werden kann, um den absoluten und kanonisierten Pfad zu einer Datei unter OS X zu finden (ähnlich wie "readlink -f" unter Linux).

In der folgenden Beispiel-Bash-Sitzung wird ein [fiktives] Dienstprogramm namens "abspath" beschrieben, das das gewünschte Verhalten aufweist:

$ 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

Wie beim letzten Aufruf von "abspath" im obigen Beispiel wäre es mir lieber, wenn Symlinks nicht automatisch aufgelöst würden, aber ich werde hier nicht zu wählerisch sein.

Michael Wehner
quelle

Antworten:

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

Beispiele:

abspath / => /

abspath /.DS_Store => /.DS_Store

abspath ~ => /Users/mschrag

cd /tmp; abspath . => /tmp

cd /; abspath .DS_Store => /.DS_Store
mschrag
quelle
11

Ich glaube nicht, dass es einen Buildin-Befehl gibt, der das macht. Jesse Wilson hat dazu ein Bash-Skript geschrieben:

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

Es funktioniert jedoch nicht gut für Pfade direkt darunter /, z. B. /etc(Drucken //etc) sowie .und ..(Drucken /cwd/.in beiden Fällen). Ich habe versucht, es zu ändern, aber mein unzureichendes Bash-Fu hat mich im Stich gelassen.

Hier ist mein Vorschlag:

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

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

Speichern Sie als /usr/bin/abspathoder so etwas und machen Sie es ausführbar. Beispielausgabe:

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

Wenn Sie eine Symlink-Auflösung wünschen, ändern Sie die printZeile wie folgt:

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

um dies zu bekommen:

Servus08:~ danielbeck$ abspath . /tmp Documents
/Users/danielbeck
/private/tmp
/Users/danielbeck/Documents
Daniel Beck
quelle
1
Ich denke, Sie sind mit dem ersten Shell-basierten Ansatz auf dem richtigen Weg, aber ich glaube, dass die Verwendung einer anderen Sprache für diesen Zweck etwas gegen den Sinn verstößt, da sie zusätzliche Abhängigkeiten einführt und so ziemlich der Kompilierung der GNU-Version von `readlink entspricht (1) 'unter OS X (sofern dies möglich ist; ich habe es noch nicht überprüft).
Michael Wehner
2
@Michael Du bist entweder auf einem System mit GNU Readlink oder auf OS X - oder? OS X hat Python im Auslieferungszustand. In der Theorie ist es eine neue Abhängigkeit, in der Praxis nicht. Jedenfalls ist es alles, was ich dir anbieten kann.
Daniel Beck
Die pragmatische Herangehensweise, die Sie implizieren, ist lobenswert, wenn auch zu simpel. Auf jeden Fall, wenn wir diesen Ansatz anstelle von etwas rein in bash geschriebenem wählen würden (was absolut machbar ist und nicht von irgendetwas anderem abhängt - es kann einfach nicht in einer Zeile gemacht werden), ist Python wahrscheinlich nicht ist nicht die beste Wahl. Sowohl Perl als auch Ruby (und wahrscheinlich PHP) können dies auf der Befehlszeile kurz und bündig ausführen, ohne dass eine tatsächliche Skriptdatei erstellt werden muss.
Michael Wehner
1
@Michael: Stimmt, aber das habe ich nicht kommentiert. Ich biete Ihnen eine 90% ige Lösung, die von Jesse Wilson in Pure Bash geschrieben wurde + die Analyse, warum es nur 90% sind. Wenn das für Sie kein Problem ist, ist das in Ordnung. Ich habe Ihnen auch eine etwas kompliziertere 100% ige Lösung in Python gegeben. Während andere Skriptsprachen möglicherweise kürzer sind (Perl infamously so :-)), erfordern alle eine zusätzliche Datei, um den Befehl zu speichern. Oder möchten Sie es jedes Mal aufschreiben, wenn Sie es verwenden möchten? Das ist auch der Grund, warum ich die Mehrzeilen-Fähigkeit hinzugefügt habe, mit mehreren Parametern umzugehen, 1 oder 2 Zeilen machen dann keinen Unterschied.
Daniel Beck
2
@Michael, warum ist Python unter OS X keine gute Wahl? Es wird sogar mit Befehlen ausgeliefert, die eigentlich Python-Skripte sind, wie zfile /usr/bin/xattr
Arjan
8

Eine Möglichkeit wäre, nur coreutils zu installieren und zu verwenden greadlink -f. Es löst Symlinks auf und funktioniert mit /Foo/oder ~/foo.txtwenn sie nicht existieren, aber nicht mit, /Foo/foo.txtwenn /Foo/sie nicht existieren.

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

Dies löst keine Symlinks auf und funktioniert auch nicht mit /Foo/foo.txt.

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 -lFührt eine Tilde-Erweiterung durch. dirs +0druckt nur das oberste Verzeichnis, wenn sich andere Verzeichnisse im Stapel befinden.

Lri
quelle
2

Ich denke, Sie könnten es entweder mit Python oder Ruby tun.

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

oder mache daraus einen Befehl mit

#!/usr/bin/env ruby
puts File.expand_path(ARGV[0])
Jay
quelle
Mein früherer Kommentar zu Daniel Becks Antwort trifft auch hier zu (ich würde eine reine Bash-y-Lösung vorziehen), aber wenn ich auf eine andere Sprache zurückgreifen würde, würde ich Ihre Lösung bisher wegen ihrer Kürze am besten mögen. :) Ich würde wahrscheinlich auch den Aufruf von ruby ​​abschließen (zB Funktion abspath () {ruby -e "setzt File.expand_path ('$ 1');}) und in mein` .profile 'einfügen.
Michael Wehner
1

Wenn Sie das File :: Spec-Modul für Perl installiert haben, können Sie dies einfach tun:

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

Für Bash / Sh-Skripte können Sie diese rekursive Funktion verwenden:

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)
Gregory Burd
quelle
Einige Variablen und Befehlsersetzungen werden nicht richtig in Anführungszeichen gesetzt. Sie könnten lokale Variablen anstelle von Variablennamen wie verwenden __filename. Das Skript verhält sich momentan dirnamesowieso eher so .
Lri
0

Installieren Sie die folgende Bibliothek für OSX:

brew install coreutils

greadlink -f file.txt
anh_ng8
quelle
0

Wenn die Installation von coreutils nicht möglich ist, werden im Folgenden Kombinationen von Symlinks behandelt. und .. und arbeitet mit Dateien und Ordnern wie 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
}

Aber es unterstützt nicht realpath's --relative-to. Dies würde /programming//a/12498485/869951 erfordern .

Oliver
quelle
0

Da die Einschränkung MacOS (zur Zeit OS X) ist, ist PHP standardmäßig verfügbar. Dadurch wird das Stammverzeichnis der Datei zurückgegeben. Entfernen Sie auch dirname, um die Datei abzurufen.

export SOURCE_DIRECTORY="$(php -r 'echo dirname(realpath($argv[1]));' -- "${BASH_SOURCE[0]}")"
danemacmillan
quelle