Cara mengambil jalur absolut file sewenang-wenang dari OS X

18

Saya sedang mencari perintah sederhana yang dapat digunakan dalam Bash untuk menemukan jalur absolut dan dikanonikkan file pada OS X (mirip dengan `` readlink -f'` di Linux).

Sesi bash sampel berikut menjelaskan utilitas [fiktif] yang disebut `` abspath'` yang memperlihatkan perilaku yang diinginkan:

$ 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

Seperti dengan doa terakhir `` abspath'` dalam contoh di atas, saya lebih suka itu tidak secara otomatis menyelesaikan symlink, tapi saya tidak akan terlalu pilih-pilih di sini.

Michael Wehner
sumber

Jawaban:

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

Contoh:

abspath / => /

abspath /.DS_Store => /.DS_Store

abspath ~ => /Users/mschrag

cd /tmp; abspath . => /tmp

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

Saya tidak berpikir ada perintah buildin yang melakukan ini. Jesse Wilson menulis skrip bash untuk ini:

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

Namun, itu tidak berfungsi dengan baik untuk jalur langsung di bawah ini /, seperti /etc(pencetakan //etc), serta .dan ..(mencetak /cwd/.dalam kedua kasus). Saya mencoba memodifikasinya, tetapi bash-fu saya yang tidak memadai gagal.

Ini saran saya:

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

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

Simpan sebagai /usr/bin/abspathatau sesuatu seperti itu dan membuatnya dapat dieksekusi. Output sampel:

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

Jika Anda melakukan ingin resolusi symlink, mengubah printbaris seperti ini:

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

untuk mendapatkan ini:

Servus08:~ danielbeck$ abspath . /tmp Documents
/Users/danielbeck
/private/tmp
/Users/danielbeck/Documents
Daniel Beck
sumber
1
Saya pikir Anda berada di jalur yang benar dengan pendekatan berbasis shell pertama, tetapi saya percaya bahwa menggunakan bahasa lain untuk melakukan hal ini agak mengecewakan tujuannya, karena memperkenalkan dependensi tambahan dan cukup banyak setara dengan hanya menyusun versi GNU dari `readlink (1) 'di bawah OS X (dengan asumsi ini bisa dilakukan; saya belum memverifikasi itu).
Michael Wehner
2
@Michael Anda berada di sistem dengan GNU readlink, atau di OS X - kan? OS X memiliki Python di luar kotak. Secara teori itu adalah ketergantungan baru, dalam praktiknya tidak. Bagaimanapun, itu yang bisa saya tawarkan kepada Anda.
Daniel Beck
Pendekatan pragmatis yang Anda maksudkan bisa dipuji, jika terlalu sederhana. Bagaimanapun, jika kita mengambil pendekatan ini sebagai pengganti dari segala sesuatu yang ditulis murni dalam bash (yang benar-benar dapat dilakukan dan tidak bergantung pada hal lain - itu tidak dapat dengan mudah dilakukan dalam satu baris), Python mungkin tidak itu pilihan terbaik. Baik Perl dan Ruby (dan mungkin PHP) dapat melakukan ini dengan ringkas pada baris perintah tanpa perlu membuat file skrip yang sebenarnya.
Michael Wehner
1
@Michael: Benar, tapi bukan itu yang saya komentari. Saya menawarkan solusi 90% yang ditulis dalam bash murni oleh Jesse Wilson + analisis mengapa hanya 90%. Jika itu tidak masalah bagi Anda, itu tidak masalah. Saya juga memberi Anda solusi yang sedikit lebih rumit, 100% dengan Python. Sementara bahasa skrip lain mungkin lebih singkat (Perl sangat terkenal :-)), semua membutuhkan file tambahan untuk menyimpan perintah. Atau apakah Anda ingin menuliskannya setiap kali ingin menggunakannya? Itu juga mengapa saya menambahkan kemampuan multi-line untuk menangani beberapa parameter, 1 atau 2 baris kemudian tidak membuat perbedaan.
Daniel Beck
2
@Michael, mengapa Python tidak menjadi pilihan yang baik di OS X? Bahkan dikirimkan dengan perintah yang sebenarnya adalah skrip Python, sepertifile /usr/bin/xattr
Arjan
8

Salah satu opsi adalah menginstal saja coreutils dan menggunakannya greadlink -f. Ini memecahkan symlink dan berfungsi dengan /Foo/atau ~/foo.txtjika mereka tidak ada, tetapi tidak dengan /Foo/foo.txtjika /Foo/tidak ada.

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

Ini tidak menyelesaikan symlink, dan tidak bekerja dengan /Foo/foo.txtbaik.

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 -lmelakukan ekspansi tilde. dirs +0hanya mencetak direktori paling atas jika ada direktori lain di tumpukan.

Lri
sumber
2

Saya kira Anda bisa melakukannya dengan python atau ruby.

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

atau buatlah perintah dengan

#!/usr/bin/env ruby
puts File.expand_path(ARGV[0])
Jay
sumber
Komentar saya sebelumnya tentang jawaban Daniel Beck juga berlaku di sini (saya lebih suka solusi Bash-y murni), meskipun jika saya harus menggunakan bahasa lain untuk mencapai ini, saya suka solusi Anda sejauh ini untuk singkatnya. :) Saya mungkin juga akan membungkus panggilan ke ruby ​​(mis. Function abspath () {ruby -e "menempatkan File.expand_path ('$ 1')";}) dan memasukkannya ke dalam `. Profil 'saya.
Michael Wehner
1

Jika Anda memiliki modul File :: Spec diinstal untuk perl Anda bisa melakukan ini:

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

Untuk skrip bash / sh Anda dapat menggunakan fungsi rekursif ini:

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
sumber
Beberapa variabel dan penggantian perintah tidak dikutip dengan benar. Anda bisa menggunakan variabel lokal daripada nama variabel suka __filename. Script saat ini berperilaku lebih seperti dirname.
Lri
0

Instal pustaka berikut untuk OSX:

brew install coreutils

greadlink -f file.txt
anh_ng8
sumber
0

Jika menginstal coreutils bukan opsi, berikut ini menangani kombo symlink,. dan .. dan bekerja pada file dan folder seperti 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
}

Tetapi itu tidak mendukung realpath - relatif-untuk. Ini membutuhkan /programming//a/12498485/869951 .

Oliver
sumber
0

Mengingat batasannya adalah MacOS (OS X pada saat itu), PHP tersedia secara default. Ini akan mengembalikan direktori root file. Hapus dirnameuntuk mendapatkan file juga.

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