Bagaimana saya bisa mendapatkan perilaku readlink -f GNU di Mac?


377

Di Linux, readlinkutilitas menerima opsi -fyang mengikuti tautan tambahan. Ini sepertinya tidak berfungsi pada Mac dan mungkin sistem berbasis BSD. Apa yang akan menjadi setara?

Berikut ini beberapa informasi debug:

$ which readlink; readlink -f
/usr/bin/readlink
readlink: illegal option -f
usage: readlink [-n] [file ...]

1
Agak terlambat, tetapi pertanyaan Anda tidak ada yang menyebutkan shell yang Anda gunakan. Ini relevan, karena readlinkdapat berupa builtin atau perintah eksternal.
0xC0000022L

1
Itu mungkin menjelaskan perbedaannya. Saya cukup yakin saya menggunakan bash pada kedua kesempatan itu.
troelskn

1
Mengapa opsi ini ilegal di Mac?
CommaToast

3
Saya benar-benar berharap Apple membuat OS X mendukung lebih banyak jalur linux default, dan menangani hal-hal seperti ini. Mereka bisa melakukannya tanpa merusak apa pun, bukan?
CommaToast

1
@ ComaToast Nah, mereka mengirim dengan Perl jadi ++ :-) ... buka Terminal.app dan ketik: touch myfile ; ln -s myfile otherfile ; perl -MCwd=abs_path -le 'print abs_path readlink(shift);' otherfile... dalam kasus saya, saya melihat: / Users / cito / myfile`. Menambahkannya ke respons saya di bawah ini. Bersulang.
G. Cito

Jawaban:


181

readlink -f melakukan dua hal:

  1. Iterates di sepanjang urutan symlinks sampai menemukan file yang sebenarnya.
  2. Ini mengembalikan nama file yang dikanonikalisasi - yaitu pathname absolutnya.

Jika Anda mau, Anda bisa membuat skrip shell yang menggunakan perilaku readlink vanilla untuk mencapai hal yang sama. Ini sebuah contoh. Jelas Anda bisa memasukkan ini ke dalam skrip Anda sendiri tempat Anda ingin meneleponreadlink -f

#!/bin/sh

TARGET_FILE=$1

cd `dirname $TARGET_FILE`
TARGET_FILE=`basename $TARGET_FILE`

# Iterate down a (possible) chain of symlinks
while [ -L "$TARGET_FILE" ]
do
    TARGET_FILE=`readlink $TARGET_FILE`
    cd `dirname $TARGET_FILE`
    TARGET_FILE=`basename $TARGET_FILE`
done

# Compute the canonicalized name by finding the physical path 
# for the directory we're in and appending the target file.
PHYS_DIR=`pwd -P`
RESULT=$PHYS_DIR/$TARGET_FILE
echo $RESULT

Perhatikan bahwa ini tidak termasuk penanganan kesalahan. Yang paling penting, ini tidak mendeteksi siklus symlink. Cara sederhana untuk melakukan ini adalah dengan menghitung berapa kali Anda berputar-putar dan gagal jika Anda menekan angka yang sangat besar, seperti 1.000.

Diedit untuk digunakan pwd -Psebagai gantinya $PWD.

Perhatikan bahwa skrip ini mengharapkan dipanggil seperti ./script_name filename, tidak -f, ubah $1menjadi $2jika Anda ingin dapat menggunakan dengan -f filenametautan baca GNU seperti.


1
Sejauh yang saya tahu, itu tidak akan berfungsi jika dir orang tua dari path adalah symlink. Misalnya. jika foo -> /var/cux, maka foo/bartidak akan diselesaikan, karena barbukan tautan, meskipun demikian foo.
troelskn

1
Ah. Iya. Ini tidak sesederhana tetapi Anda dapat memperbarui skrip di atas untuk mengatasinya. Saya akan mengedit (menulis ulang, sungguh) jawabannya.
Keith Smith

Ya, sebuah tautan bisa berada di mana saja di jalur. Saya kira skrip dapat beralih di setiap bagian jalan, tetapi kemudian menjadi sedikit rumit.
troelskn

1
Terima kasih. Masalahnya adalah $ PWD memberi kita direktori kerja logis, berdasarkan nilai-nilai di symlink yang telah kita ikuti. Kita bisa mendapatkan direktori fisik nyata dengan 'pwd -P' Ini harus menghitungnya dengan mengejar ".." hingga ke akar dari sistem file. Saya akan memperbarui skrip dalam jawaban saya sesuai.
Keith Smith

12
Solusi hebat, tetapi tidak berurusan dengan jalur yang perlu diloloskan , seperti nama file atau direktori dengan ruang yang disematkan . Untuk mengatasinya, gunakan cd "$(dirname "$TARGET_FILE")" dan TARGET_FILE=$(readlink "$TARGET_FILE")dan TARGET_FILE=$(basename "$TARGET_FILE")di tempat yang sesuai dalam kode di atas.
mklement0

439

MacPorts dan Homebrew menyediakan coreutils paket berisi greadlink(GNU readlink). Kredit ke posting Michael Kallweitt di mackb.com.

brew install coreutils

greadlink -f file.txt

Jika Anda memanggil readlink dari tempat lain selain bash, solusi lain adalah menghapus / usr / bin / readlink kemudian membuat tautan ke / usr / local / bin / greadlink.
chinglun

8
Tolong jangan, kecuali Anda a) menulis perangkat lunak untuk Anda sendiri b) ingin mengacaukan orang lain. Tulis sedemikian rupa sehingga "hanya bekerja" untuk siapa saja.
kgadek

14
@ching Lakukan ini sebagai gantinya di .bashrc:export PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
bfontaine

1
Pertanyaannya meminta yang setara pada sistem OS X atau BSD, ini tidak menjawab itu. Anda juga harus menginstal Homebrew. Anda juga akan menginstal banyak hal selain dari GNU Readlink. Jika Anda menulis skrip untuk pemasangan OS X standar, jawaban ini tidak akan membantu. Anda dapat menggunakan kombinasi pwd -Pdan readlinkpada OS X, tanpa menginstal apa pun
Jason S

4
Saya ingin GNU dan utilitas lain di Mac saya yang saya percaya melakukannya dengan baik, tanpa awalan, dan mudah diakses dari pengguna saya PATH. Apakah di atas dimulai dengan brew install <package> --default-names. Sering kali pertanyaan di situs ini sedikit dijawab, atau lebih tepatnya, di luar persyaratan teknis penanya, tetapi dalam situasi yang sama, dan masih sangat membantu. topbug.net/blog/2013/04/14/…
Pysis

43

Anda mungkin tertarik realpath(3), atau Python os.path.realpath. Keduanya tidak persis sama; panggilan pustaka C mengharuskan komponen jalur perantara ada, sedangkan versi Python tidak.

$ pwd
/tmp/foo
$ ls -l
total 16
-rw-r--r--  1 miles    wheel  0 Jul 11 21:08 a
lrwxr-xr-x  1 miles    wheel  1 Jul 11 20:49 b -> a
lrwxr-xr-x  1 miles    wheel  1 Jul 11 20:49 c -> b
$ python -c 'import os,sys;print(os.path.realpath(sys.argv[1]))' c
/private/tmp/foo/a

Saya tahu Anda mengatakan Anda lebih suka sesuatu yang lebih ringan daripada bahasa scripting lain, tetapi kalau-kalau kompilasi biner tidak dapat disangkal, Anda dapat menggunakan Python dan ctypes (tersedia di Mac OS X 10.5) untuk membungkus panggilan perpustakaan:

#!/usr/bin/python

import ctypes, sys

libc = ctypes.CDLL('libc.dylib')
libc.realpath.restype = ctypes.c_char_p
libc.__error.restype = ctypes.POINTER(ctypes.c_int)
libc.strerror.restype = ctypes.c_char_p

def realpath(path):
    buffer = ctypes.create_string_buffer(1024) # PATH_MAX
    if libc.realpath(path, buffer):
        return buffer.value
    else:
        errno = libc.__error().contents.value
        raise OSError(errno, "%s: %s" % (libc.strerror(errno), buffer.value))

if __name__ == '__main__':
    print realpath(sys.argv[1])

Ironisnya, versi C dari skrip ini seharusnya lebih pendek. :)


Ya, realpathmemang itulah yang saya inginkan. Tapi sepertinya agak canggung bahwa saya harus mengkompilasi biner untuk mendapatkan fungsi ini dari skrip shell.
troelskn

4
Mengapa tidak menggunakan Python one-liner dalam skrip shell? (Tidak begitu berbeda dari panggilan satu-line untuk readlinkdirinya sendiri, bukan?)
Telemachus

3
python -c "import os,sys; print(os.path.realpath(os.path.expanduser(sys.argv[1])))" "${1}" bekerja dengan jalur seperti '~/.symlink'
Wes Turner

@troelskin Saya tidak menyadari Perl, Python, dll. "diizinkan" !! ... dalam hal ini saya akan menambahkan perl -MCwd=abs_path -le 'print abs_path readlink(shift);'jawaban saya :-)
G. Cito

27

Saya benci untuk melanjutkan dengan implementasi lain, tetapi saya membutuhkan a) implementasi portabel, shell murni , dan b) cakupan unit-test , karena jumlah kasus tepi untuk sesuatu seperti ini adalah non-sepele .

Lihat proyek saya di Github untuk tes dan kode lengkap. Berikut ini adalah sinopsis implementasi:

Seperti yang ditunjukkan Keith Smith dengan cerdas, readlink -flakukan dua hal: 1) menyelesaikan symlink secara rekursif, dan 2) mengkanoniskan hasilnya, dengan demikian:

realpath() {
    canonicalize_path "$(resolve_symlinks "$1")"
}

Pertama, implementasi resollink symlink:

resolve_symlinks() {
    local dir_context path
    path=$(readlink -- "$1")
    if [ $? -eq 0 ]; then
        dir_context=$(dirname -- "$1")
        resolve_symlinks "$(_prepend_path_if_relative "$dir_context" "$path")"
    else
        printf '%s\n' "$1"
    fi
}

_prepend_path_if_relative() {
    case "$2" in
        /* ) printf '%s\n' "$2" ;;
         * ) printf '%s\n' "$1/$2" ;;
    esac 
}

Perhatikan bahwa ini adalah versi implementasi penuh yang sedikit disederhanakan . Implementasi penuh menambahkan cek kecil untuk siklus symlink , serta memijat output sedikit.

Akhirnya, fungsi untuk mengkanonik jalur:

canonicalize_path() {
    if [ -d "$1" ]; then
        _canonicalize_dir_path "$1"
    else
        _canonicalize_file_path "$1"
    fi
}   

_canonicalize_dir_path() {
    (cd "$1" 2>/dev/null && pwd -P) 
}           

_canonicalize_file_path() {
    local dir file
    dir=$(dirname -- "$1")
    file=$(basename -- "$1")
    (cd "$dir" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$file")
}

Itu dia, kurang lebih. Cukup sederhana untuk disisipkan ke skrip Anda, tetapi cukup rumit sehingga Anda akan gila untuk mengandalkan kode apa pun yang tidak memiliki tes unit untuk kasus penggunaan Anda.


3
Ini bukan POSIX - ini menggunakan localkata kunci non-POSIX
go2null

Ini tidak setara dengan readlink -f, atau dari realpath. Dengan asumsi Mac dengan formula coreutils terinstal (demikian dengan greadlinktersedia), coba ini dengan ln -s /tmp/linkeddir ~/Documents; greadlink -f /tmp/linkeddir/..mencetak direktori home Anda (itu menyelesaikan /tmp/linkeddirtautan sebelum menerapkan ..dir induk), tetapi kode Anda menghasilkan /private/tmp/as /tmp/linkeddir/..bukan symlink, tetapi -d /tmp/linkeddir/..benar dan cd /tmp/linkeddir/..membawa Anda ke /tmp, yang jalur kanoniknya adalah /private/tmp. Kode Anda melakukan apa yang harus realpath -Ldilakukan.
Martijn Pieters

27

Satu-liner sederhana di perl yang pasti akan bekerja hampir di mana saja tanpa ketergantungan eksternal:

perl -MCwd -e 'print Cwd::abs_path shift' ~/non-absolute/file

Will dereference symlinks.

Penggunaan dalam skrip bisa seperti ini:

readlinkf(){ perl -MCwd -e 'print Cwd::abs_path shift' "$1";}
ABSPATH="$(readlinkf ./non-absolute/file)"

4
untuk mendapatkan baris baru tambahan -l, tambahkan , sepertiperl -MCwd -le 'print Cwd::abs_path shift' ~/non-absolute/file
pjvandehaar

26
  1. Pasang homebrew
  2. Jalankan "brew install coreutils"
  3. Jalankan "greadlink -f path"

greadlink adalah readlink gnu yang mengimplementasikan -f. Anda dapat menggunakan macports atau lainnya juga, saya lebih suka homebrew.


2
Saya tidak bisa melihat perbedaan pada jawaban yang diberikan pada 2010 stackoverflow.com/a/4031502/695671
Jason S

Jika Anda perlu menggunakan perintah ini dengan nama normalnya, Anda dapat menambahkan direktori "gnubin" ke PATH Anda dari bashrc seperti: PATH = "/ usr / local / opt / coreutils / libexec / gnubin: $ PATH"
Denis Trofimov

20

Saya membuat sebuah skrip bernama realpath pribadi yang terlihat seperti:

#!/usr/bin/env python
import os.sys
print os.path.realpath(sys.argv[1])

Anda harus mengubahnya menjadi sys.argv[1]jika Anda ingin skrip untuk mencetak jalur sebenarnya dari argumen pertama. sys.argv[0]hanya mencetak jalur sebenarnya dari skrip pyton itu sendiri, yang tidak terlalu berguna.
Jakob Egger

17
Ini versi aliasnya, untuk ~/.profile:alias realpath="python -c 'import os, sys; print os.path.realpath(sys.argv[1])'"
jwhitlock

Jawaban yang bagus, dan @jwhitlock satu-liner yang bagus. Sejak OP dikutip readlink -f, berikut ini adalah versi modifikasi alias @jwhitlock untuk mendukung kemungkinan -fflag: alias realpath="python -c 'import os, sys; print os.path.realpath(sys.argv[2] if sys.argv[1] == \"-f\" else sys.argv[1])'"
codesniffer

11

Bagaimana dengan ini?

function readlink() {
  DIR="${1%/*}"
  (cd "$DIR" && echo "$(pwd -P)")
}

Ini adalah satu-satunya solusi yang berfungsi untuk saya; Saya perlu mengurai jalur seperti ../../../->/
keflavich

ini tidak dapat bekerja jika Anda memiliki symlink /usr/bin/misalnya seperti alternativessistem suka miliki.
akostadinov

Tidak berfungsi untuk file individual, hanya direktori. GNU readlink berfungsi dengan direktori, file, symlink, dll.
codesniffer

7

Ini adalah fungsi shell portabel yang bisa digunakan di APAPUN shell sebanding Bourne. Ini akan menyelesaikan tanda baca jalur relatif ".. atau." dan tautan simbolik dereferensi.

Jika karena alasan tertentu Anda tidak memiliki perintah realpath (1), atau readlink (1) ini dapat alias.

which realpath || alias realpath='real_path'

Nikmati:

real_path () {
  OIFS=$IFS
  IFS='/'
  for I in $1
  do
    # Resolve relative path punctuation.
    if [ "$I" = "." ] || [ -z "$I" ]
      then continue
    elif [ "$I" = ".." ]
      then FOO="${FOO%%/${FOO##*/}}"
           continue
      else FOO="${FOO}/${I}"
    fi

    ## Resolve symbolic links
    if [ -h "$FOO" ]
    then
    IFS=$OIFS
    set `ls -l "$FOO"`
    while shift ;
    do
      if [ "$1" = "->" ]
        then FOO=$2
             shift $#
             break
      fi
    done
    IFS='/'
    fi
  done
  IFS=$OIFS
  echo "$FOO"
}

juga, kalau-kalau ada orang yang tertarik di sini adalah bagaimana menerapkan nama samaran dan dirname dalam kode shell 100% murni:

## http://www.opengroup.org/onlinepubs/000095399/functions/dirname.html
# the dir name excludes the least portion behind the last slash.
dir_name () {
  echo "${1%/*}"
}

## http://www.opengroup.org/onlinepubs/000095399/functions/basename.html
# the base name excludes the greatest portion in front of the last slash.
base_name () {
  echo "${1##*/}"
}

Anda dapat menemukan versi terbaru dari kode shell ini di situs google saya: http://sites.google.com/site/jdisnard/realpath

EDIT: Kode ini dilisensikan berdasarkan ketentuan lisensi 2-klausa (gaya freeBSD). Salinan lisensi dapat ditemukan dengan mengikuti hyperlink di atas ke situs saya.


Real_path mengembalikan / nama file bukan / path / ke / nama file pada Mac OS X Lion di bash shell.

@ Burry Hal yang sama terjadi pada OSX Snow Leopard. Lebih buruk, panggilan berturut-turut ke real_path () hasil gabungan!
Dominique

@ Dominique: tidak mengherankan, karena bagian dalam fungsi tidak berjalan dalam subkulitnya sendiri ... memang jelek dan rapuh.
0xC0000022L

7

FreeBSD dan OSX memiliki versi statturunan dari NetBSD.

Anda dapat menyesuaikan output dengan sakelar format (lihat halaman manual pada tautan di atas).

%  cd  /service
%  ls -tal 
drwxr-xr-x 22 root wheel 27 Aug 25 10:41 ..
drwx------  3 root wheel  8 Jun 30 13:59 .s6-svscan
drwxr-xr-x  3 root wheel  5 Jun 30 13:34 .
lrwxr-xr-x  1 root wheel 30 Dec 13 2013 clockspeed-adjust -> /var/service/clockspeed-adjust
lrwxr-xr-x  1 root wheel 29 Dec 13 2013 clockspeed-speed -> /var/service/clockspeed-speed
% stat -f%R  clockspeed-adjust
/var/service/clockspeed-adjust
% stat -f%Y  clockspeed-adjust
/var/service/clockspeed-adjust

Beberapa versi OS X stat mungkin tidak memiliki -f%Ropsi untuk format. Dalam hal ini -stat -f%Ymungkin sudah cukup. The -f%Ypilihan akan menunjukkan target symlink, sedangkan-f%R menunjukkan nama path absolut sesuai dengan file.

EDIT:

Jika Anda dapat menggunakan Perl (Darwin / OS X dilengkapi dengan versi terbaru dari perl) maka:

perl -MCwd=abs_path -le 'print abs_path readlink(shift);' linkedfile.txt

akan bekerja.


2
Ini berlaku pada FreeBSD 10, tetapi tidak pada OS X 10.9.
Zanchey

Tangkapan yang bagus. The Stat Halaman panduan dikutip adalah untuk OS / X 10.9. The Rpilihan untuk -f%hilang. Mungkin stat -f%Ymemberikan output yang diinginkan. Saya akan menyesuaikan jawabannya. Perhatikan bahwa statalat ini muncul di FreeBSD di versi 4.10 dan NetBSD di versi 1.6.
G. Cito

Perintah Perl tidak bekerja dengan benar pada 10.15. Berikan file dalam direktori saat ini dan berikan direktori saat ini (tanpa file).
codesniffer

7

Cara termudah untuk menyelesaikan masalah ini dan mengaktifkan fungsionalitas readlink pada Mac dengan Homebrew yang diinstal atau FreeBSD adalah dengan menginstal paket 'coreutils'. Mungkin juga diperlukan pada distribusi Linux tertentu dan OS POSIX lainnya.

Sebagai contoh, di FreeBSD 11, saya menginstal dengan memohon:

# pkg install coreutils

Pada MacOS dengan Homebrew, perintahnya adalah:

$ brew install coreutils

Tidak begitu yakin mengapa jawaban lain begitu rumit, hanya itu yang ada di sana. File-file tidak di tempat yang berbeda, mereka hanya belum diinstal.


7
brew install coreutils --with-default-namesuntuk lebih spesifik. Atau Anda akan berakhir dengan "greadlink" sebagai gantinya ...
Henk

Saya tidak dapat melihat perbedaan pada jawaban dari 2010 stackoverflow.com/a/4031502/695671 dan 2012 stackoverflow.com/a/9918368/695671 Pertanyaannya sebenarnya adalah "... pada Mac dan mungkin sistem berbasis BSD. Apa yang akan setara dengan? " Saya tidak percaya ini adalah jawaban yang sangat bagus untuk pertanyaan itu. Banyak alasan, tetapi Anda mungkin menargetkan mesin Mac OS tanpa kemampuan untuk menginstal apa pun. pwd -Pcukup sederhana tetapi menghilang di antara banyak jawaban lain, termasuk yang diulang, maka downvote.
Jason S

3

Mulai Perbarui

Ini adalah masalah yang sering terjadi sehingga kami mengumpulkan perpustakaan Bash 4 untuk penggunaan gratis (Lisensi MIT) yang disebut realpath-lib . Ini dirancang untuk mengemulasi readlink -f secara default dan menyertakan dua test suite untuk memverifikasi (1) bahwa ia berfungsi untuk sistem unix yang diberikan dan (2) terhadap readlink -f jika diinstal (tetapi ini tidak diperlukan). Selain itu, ini dapat digunakan untuk menyelidiki, mengidentifikasi, dan melepaskan symlink yang dalam dan rusak, sehingga dapat menjadi alat yang berguna untuk mendiagnosis masalah fisik dan simbolik direktori dan file yang bersarang. Hal ini dapat ditemukan di github.com atau bitbucket.org .

Akhiri Pembaruan

Solusi lain yang sangat ringkas dan efisien yang tidak bergantung pada apa pun kecuali Bash adalah:

function get_realpath() {

    [[ ! -f "$1" ]] && return 1 # failure : file does not exist.
    [[ -n "$no_symlinks" ]] && local pwdp='pwd -P' || local pwdp='pwd' # do symlinks.
    echo "$( cd "$( echo "${1%/*}" )" 2>/dev/null; $pwdp )"/"${1##*/}" # echo result.
    return 0 # success

}

Ini juga termasuk pengaturan lingkungan no_symlinksyang menyediakan kemampuan untuk menyelesaikan symlink ke sistem fisik. Selama no_symlinksdiatur ke sesuatu, yaitu no_symlinks='on'symlink akan diselesaikan ke sistem fisik. Kalau tidak mereka akan diterapkan (pengaturan default).

Ini harus bekerja pada sistem apa pun yang menyediakan Bash, dan akan mengembalikan kode keluar yang kompatibel dengan Bash untuk tujuan pengujian.


3

Sudah ada banyak jawaban, tetapi tidak ada yang berhasil untuk saya ... Jadi ini yang saya gunakan sekarang.

readlink_f() {
  local target="$1"
  [ -f "$target" ] || return 1 #no nofile

  while [ -L "$target" ]; do
    target="$(readlink "$target")" 
  done
  echo "$(cd "$(dirname "$target")"; pwd -P)/$target"
}

1
Ini tidak berfungsi jika $targetpath, hanya berfungsi jika itu file.
MaxGhost

3

Cara malas yang bekerja untuk saya,

$ brew install coreutils
$ ln -s /usr/local/bin/greadlink /usr/local/bin/readlink
$ which readlink
/usr/local/bin/readlink
/usr/bin/readlink

1

Lebih baik terlambat daripada tidak, kurasa. Saya termotivasi untuk mengembangkan ini secara khusus karena skrip Fedora saya tidak berfungsi pada Mac. Masalahnya adalah dependensi dan Bash. Mac tidak memilikinya, atau jika ya, sering ada di tempat lain (jalur lain). Manipulasi jalur ketergantungan dalam skrip Bash lintas-platform adalah sakit kepala paling-paling dan risiko keamanan paling buruk - jadi sebaiknya hindari penggunaannya, jika mungkin.

Fungsi get_realpath () di bawah ini sederhana, Bash-centric, dan tidak ada ketergantungan yang diperlukan. Saya hanya menggunakan echo dan cd bawaan Bash . Ini juga cukup aman, karena semuanya diuji pada setiap tahap dan mengembalikan kondisi kesalahan.

Jika Anda tidak ingin mengikuti symlink, maka letakkan set -P di bagian depan skrip, tetapi jika tidak cd akan menyelesaikan symlink secara default. Ini telah diuji dengan argumen file yang {absolute | kerabat | symlink | local} dan mengembalikan path absolut ke file. Sejauh ini kami tidak punya masalah dengan itu.

function get_realpath() {

if [[ -f "$1" ]]
then
    # file *must* exist
    if cd "$(echo "${1%/*}")" &>/dev/null
    then
        # file *may* not be local
        # exception is ./file.ext
        # try 'cd .; cd -;' *works!*
        local tmppwd="$PWD"
        cd - &>/dev/null
    else
        # file *must* be local
        local tmppwd="$PWD"
    fi
else
    # file *cannot* exist
    return 1 # failure
fi

# reassemble realpath
echo "$tmppwd"/"${1##*/}"
return 0 # success

}

Anda dapat menggabungkan ini dengan fungsi lain get_dirname, get_filename, get_stemname dan validate_path. Ini dapat ditemukan di repositori GitHub kami sebagai realpath-lib (pengungkapan penuh - ini adalah produk kami tetapi kami menawarkannya secara gratis kepada komunitas tanpa batasan). Itu juga bisa berfungsi sebagai alat instruksional - itu didokumentasikan dengan baik.

Kami telah mencoba yang terbaik untuk menerapkan praktik yang disebut 'Bash modern', tetapi Bash adalah subjek besar dan saya yakin akan selalu ada ruang untuk perbaikan. Ini membutuhkan Bash 4+ tetapi bisa dibuat untuk bekerja dengan versi yang lebih lama jika masih ada.


1

Karena pekerjaan saya digunakan oleh orang-orang dengan Linux non-BSD dan juga macOS, saya telah memilih untuk menggunakan alias ini dalam skrip build kami ( sedtermasuk karena memiliki masalah yang sama):

##
# If you're running macOS, use homebrew to install greadlink/gsed first:
#   brew install coreutils
#
# Example use:
#   # Gets the directory of the currently running script
#   dotfilesDir=$(dirname "$(globalReadlink -fm "$0")")
#   alias al='pico ${dotfilesDir}/aliases.local'
##

function globalReadlink () {
  # Use greadlink if on macOS; otherwise use normal readlink
  if [[ $OSTYPE == darwin* ]]; then
    greadlink "$@"
  else
    readlink "$@"
  fi
}

function globalSed () {
  # Use gsed if on macOS; otherwise use normal sed
  if [[ $OSTYPE == darwin* ]]; then
    gsed "$@"
  else
    sed "$@"
  fi
}

Pemeriksaan opsional yang dapat Anda tambahkan untuk menginstal dependensi homebrew + coreutils secara otomatis :

if [[ "$OSTYPE" == "darwin"* ]]; then
  # Install brew if needed
  if [ -z "$(which brew)" ]; then 
    /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"; 
  fi
  # Check for coreutils
  if [ -z "$(brew ls coreutils)" ]; then
    brew install coreutils
  fi
fi

Saya kira benar-benar "global" perlu memeriksa orang lain ... tapi itu mungkin mendekati tanda 80/20.


1

Penjelasan

coreutils adalah brewpaket yang menginstal utilitas inti GNU / Linux yang sesuai dengan implementasi Mac OSX sehingga Anda dapat menggunakannya

Anda mungkin menemukan program atau utilitas pada sistem mac osx Anda yang tampaknya mirip dengan Linux coreutils ("Core Utilities") namun mereka berbeda dalam beberapa hal (seperti memiliki flag yang berbeda).

Ini karena implementasi Mac OSX dari alat-alat ini berbeda. Untuk mendapatkan perilaku mirip GNU / Linux asli, Anda dapat menginstal coreutilspaket melalui brewsistem manajemen paket.

Ini akan menginstal utilitas inti yang sesuai, diawali oleh g. Misalnya untuk readlink, Anda akan menemukan greadlinkprogram yang sesuai .

Untuk membuat readlinkkinerja seperti implementasi GNU readlink( greadlink), Anda dapat membuat alias sederhana setelah Anda menginstal coreutils.

Penerapan

  1. Pasang brew

Ikuti instruksi di https://brew.sh/

  1. Instal paket coreutils

brew install coreutils

  1. Buat Alias

Anda dapat menempatkan alias Anda di ~ / .bashrc, ~ / .bash_profile, atau di mana pun Anda terbiasa menyimpan alias bash Anda. Saya pribadi menyimpan milik saya di ~ / .bashrc

alias readlink=greadlink

Anda dapat membuat alias serupa untuk coreutils lainnya seperti gmv, gdu, gdf, dan sebagainya. Tetapi berhati-hatilah bahwa perilaku GNU pada mesin mac mungkin membingungkan bagi orang lain yang terbiasa bekerja dengan coreutils asli, atau mungkin berperilaku dengan cara yang tidak terduga pada sistem mac Anda.


0

Saya menulis utilitas realpath untuk OS X yang dapat memberikan hasil yang sama seperti readlink -f.


Berikut ini sebuah contoh:

(jalcazar@mac tmp)$ ls -l a
lrwxrwxrwx 1 jalcazar jalcazar 11  8 25 19:29 a -> /etc/passwd

(jalcazar@mac tmp)$ realpath a
/etc/passwd


Jika Anda menggunakan MacPorts, Anda dapat menginstalnya dengan perintah berikut: sudo port selfupdate && sudo port install realpath.


0

Benar-benar platform-indpendent juga akan menjadi R-onliner ini

readlink(){ RScript -e "cat(normalizePath(commandArgs(T)[1]))" "$1";}

Untuk benar-benar meniru readlink -f <path>, $ 2 dan bukan $ 1 harus digunakan.


Kecuali R hampir tidak pada sistem apa pun secara default. Belum di Mac (per 10.15) tentang pertanyaan ini.
codesniffer

0

readlink -fImplementasi yang sesuai dengan POSIX untuk skrip shell POSIX

https://github.com/ko1nksm/readlinkf

Ini adalah POSIX compliant (tidak ada bashism). Tidak menggunakan juga readlinktidak realpath. Saya telah memverifikasi bahwa persis sama dengan membandingkan dengan GNU readlink -f(lihat hasil tes ). Ini memiliki penanganan kesalahan dan kinerja yang baik. Anda dapat dengan aman mengganti dari readlink -f. Lisensi tersebut adalah CC0, sehingga Anda dapat menggunakannya untuk proyek apa pun.

# POSIX compliant version
readlinkf_posix() {
  [ "${1:-}" ] || return 1
  target=$1 max_symlinks=10 CDPATH=''

  [ -e "${target%/}" ] || target=${1%"${1##*[!/]}"} # trim trailing slashes
  [ -d "${target:-/}" ] && target="$target/"

  cd -P . 2>/dev/null || return 1
  while [ "$max_symlinks" -gt 0 ] && max_symlinks=$((max_symlinks - 1)); do
    if [ ! "$target" = "${target%/*}" ]; then
      cd -P "${target%/*}/" 2>/dev/null || break
      target=${target##*/}
    fi

    if [ ! -L "$target" ]; then
      target="${PWD%/}${target:+/}$target"
      printf '%s\n' "${target:-/}"
      return 0
    fi

    # `ls -dl` format: "%s %u %s %s %u %s %s -> %s\n",
    #   <file mode>, <number of links>, <owner name>, <group name>,
    #   <size>, <date and time>, <pathname of link>, <contents of link>
    # https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ls.html
    link=$(ls -dl "$target" 2>/dev/null) || break
    target=${link#*" $target -> "}
  done
  return 1
}

Silakan merujuk ke kode terbaru. Mungkin beberapa diperbaiki.


-2

Perl memiliki fungsi readlink (mis. Bagaimana cara menyalin tautan simbolik di Perl? ). Ini berfungsi di sebagian besar platform, termasuk OS X:

perl -e "print readlink '/path/to/link'"

Sebagai contoh:

$ mkdir -p a/b/c
$ ln -s a/b/c x
$ perl -e "print readlink 'x'"
a/b/c

5
Ini melakukan hal yang sama seperti readlinktanpa -fopsi - tidak ada rekursi, tidak ada jalur absolut -, jadi sebaiknya Anda gunakan readlinksecara langsung.
mklement0

-2

Jawaban dari @Keith Smith memberikan loop tak terbatas.

Inilah jawaban saya, yang saya gunakan hanya pada SunOS (SunOS kehilangan begitu banyak perintah POSIX dan GNU).

Ini adalah file skrip yang harus Anda masukkan ke dalam salah satu dari direktori $ PATH Anda:

#!/bin/sh
! (($#)) && echo -e "ERROR: readlink <link to analyze>" 1>&2 && exit 99

link="$1"
while [ -L "$link" ]; do
  lastLink="$link"
  link=$(/bin/ls -ldq "$link")
  link="${link##* -> }"
  link=$(realpath "$link")
  [ "$link" == "$lastlink" ] && echo -e "ERROR: link loop detected on $link" 1>&2 && break
done

echo "$link"


-5

Path ke readlink berbeda antara sistem saya dan Anda. Silakan coba tentukan path lengkap:

/sw/sbin/readlink -f


1
Dan apa - tepatnya - perbedaan antara / sw / sbin dan / usr / bin? Dan mengapa kedua binari itu berbeda?
troelskn

4
Aha .. jadi fink berisi pengganti readlinkgnu yang kompatibel. Itu menyenangkan untuk diketahui, tetapi itu tidak menyelesaikan masalah saya, karena saya perlu skrip saya untuk dijalankan pada mesin orang lain, dan saya tidak bisa meminta mereka untuk menginstal fink untuk itu.
troelskn

Anda tidak menjawab pertanyaan awal. Harap tulis ulang jawaban Anda.
Jaime Hablutzel
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.