Jak pobrać bezwzględną ścieżkę dowolnego pliku z OS X.

18

Szukam prostego polecenia, którego można użyć w Bash, aby znaleźć absolutną i kanonizowaną ścieżkę do pliku w systemie OS X (podobnym do `` readlink -f '' w systemie Linux).

Następująca przykładowa sesja bash opisuje [fikcyjne] narzędzie o nazwie `` abspath '', które wykazuje pożądane zachowanie:

$ 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

Podobnie jak w przypadku ostatniego wywołania `` abspath '' w powyższym przykładzie, wolałbym, aby nie rozpoznawał automatycznie dowiązań symbolicznych, ale nie będę tutaj zbyt wybredny.

Michael Wehner
źródło

Odpowiedzi:

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

Przykłady:

abspath / => /

abspath /.DS_Store => /.DS_Store

abspath ~ => /Users/mschrag

cd /tmp; abspath . => /tmp

cd /; abspath .DS_Store => /.DS_Store
mschrag
źródło
11

Nie sądzę, że istnieje wbudowane polecenie, które to robi. Jesse Wilson napisał do tego skrypt bash:

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

Jednak nie działa dobrze w przypadku ścieżek bezpośrednio poniżej /, takich jak /etc(drukowanie //etc), a także .i ..(drukowanie /cwd/.w obu przypadkach). Próbowałem to zmodyfikować, ale moje niewystarczające bash-fu mnie zawiodło.

Oto moja sugestia:

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

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

Zapisz jako /usr/bin/abspathlub coś w tym stylu i uczyń go wykonywalnym. Przykładowe dane wyjściowe:

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

Jeśli zrobić chcą rozdzielczość symlink, zmienić printlinię:

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

aby to uzyskać:

Servus08:~ danielbeck$ abspath . /tmp Documents
/Users/danielbeck
/private/tmp
/Users/danielbeck/Documents
Daniel Beck
źródło
1
Myślę, że jesteś na dobrej drodze z pierwszym podejściem opartym na powłoce, ale wierzę, że użycie do tego celu innego języka nieco przeczy celowi, ponieważ wprowadza dodatkowe zależności i jest prawie równoważne z kompilacją wersji GNU `readlink (1) „w OS X (zakładając, że można to zrobić; jeszcze tego nie zweryfikowałem).
Michael Wehner,
2
@Michael Jesteś na systemie z linkiem do odczytu GNU lub na OS X - prawda? OS X ma Python po wyjęciu z pudełka. Teoretycznie jest to nowa zależność, w praktyce nie. W każdym razie to wszystko, co mogę ci zaoferować.
Daniel Beck
Pragmatyczne podejście, które sugerujesz, jest godne pochwały, choć zbyt uproszczone. W każdym razie, gdybyśmy przyjęli to podejście zamiast czegoś napisanego wyłącznie w bashu (co jest absolutnie wykonalne i nie zależy od niczego innego - nie można tego łatwo zrobić w jednym wierszu), prawdopodobnie Python nie jest najlepszy wybór. Zarówno Perl, jak i Ruby (i prawdopodobnie PHP) mogą to zrobić zwięźle w wierszu poleceń bez potrzeby tworzenia rzeczywistego pliku skryptu.
Michael Wehner,
1
@Michael: To prawda, ale nie o tym komentowałem. Oferuję 90% rozwiązanie napisane czystym bashiem przez Jesse Wilsona + analiza, dlaczego jest to tylko 90%. Jeśli to nie problem, nie ma sprawy. Dałem ci również nieco bardziej skomplikowane, 100% rozwiązanie w Pythonie. Podczas gdy inne języki skryptowe mogą być krótsze (Perl niesławnie, więc :-)), wszystkie wymagają dodatkowego pliku do przechowywania polecenia. A może chcesz go wypisywać za każdym razem, gdy chcesz go używać? Dlatego też dodałem możliwość wielu linii do obsługi wielu parametrów, 1 lub 2 linii, więc nie robię inaczej.
Daniel Beck
2
@Michael, dlaczego Python nie byłby dobrym wyborem na OS X? Jest nawet dostarczany z poleceniami, które w rzeczywistości są skryptami Pythona, takimi jakfile /usr/bin/xattr
Arjan
8

Jedną z opcji byłoby po prostu zainstalowanie coreutils i użycie greadlink -f. Rozwiązuje dowiązania symboliczne i działa z /Foo/lub ~/foo.txtjeśli nie istnieją, ale nie z /Foo/foo.txtjeśli /Foo/nie istnieje.

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

Nie rozwiązuje to dowiązań symbolicznych i nie działa z /Foo/foo.txtżadnym z nich.

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 -lwykonuje rozwinięcie tyldy. dirs +0drukuje tylko najwyższy katalog, jeśli na stosie znajdują się inne katalogi.

Lri
źródło
2

Myślę, że możesz to zrobić za pomocą Pythona lub Ruby.

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

lub uczyń to poleceniem za pomocą

#!/usr/bin/env ruby
puts File.expand_path(ARGV[0])
Sójka
źródło
Dotyczy to również mojego wcześniejszego komentarza do odpowiedzi Daniela Becka (wolałbym rozwiązanie oparte wyłącznie na języku Bash-y), chociaż jeśli miałbym skorzystać z innego języka, osiągnij to, jak dotąd najbardziej podoba mi się twoje rozwiązanie. :) Prawdopodobnie również zawinę wywołanie ruby ​​(np. Funkcja abspath () {ruby -e "wstawia File.expand_path ('$ 1')";}) i umieszczam w moim `.profile '.
Michael Wehner,
1

Jeśli masz zainstalowany moduł Perla :: File :: Spec, możesz po prostu to zrobić:

perl -MFile::Spec -e 'print File::Spec->rel2abs("../however/complex/../you/want/to.get"), "\n"'
Cwaniak
źródło
0

W przypadku skryptów bash / sh możesz użyć tej funkcji rekurencyjnej:

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
źródło
Niektóre zmienne i podstawienia poleceń nie są poprawnie cytowane. Możesz użyć zmiennych lokalnych zamiast nazw zmiennych takich jak __filename. Skrypt i tak zachowuje się bardziej podobnie dirname.
Lri,
0

Zainstaluj następującą bibliotekę dla OSX:

brew install coreutils

greadlink -f file.txt
anh_ng8
źródło
0

Jeśli instalacja coreutils nie jest opcją, poniższe polecenia obsługują kombinacje dowiązań symbolicznych,. i .. i działa na plikach i folderach takich jak 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
}

Ale nie obsługuje realpath - w odniesieniu do. Wymagałoby to https://stackoverflow.com/a/12498485/869951 .

Oliver
źródło
0

Biorąc pod uwagę, że ograniczeniem jest MacOS (w tym czasie OS X), PHP jest domyślnie dostępne. Spowoduje to zwrócenie katalogu głównego pliku. Usuń również, dirnameaby pobrać plik.

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