Diberikan jalur absolut atau relatif (dalam sistem mirip Unix), saya ingin menentukan jalur lengkap target setelah menyelesaikan setiap symlink perantara. Poin bonus untuk menyelesaikan notasi ~ nama pengguna pada saat yang bersamaan.
Jika targetnya adalah direktori, mungkin chdir () masuk ke direktori dan kemudian memanggil getcwd (), tapi saya benar-benar ingin melakukan ini dari skrip shell daripada menulis C helper. Sayangnya, shell memiliki kecenderungan untuk mencoba menyembunyikan keberadaan symlink dari pengguna (ini bash di OS X):
$ ls -ld foo bar
drwxr-xr-x 2 greg greg 68 Aug 11 22:36 bar
lrwxr-xr-x 1 greg greg 3 Aug 11 22:36 foo -> bar
$ cd foo
$ pwd
/Users/greg/tmp/foo
$
Yang saya inginkan adalah fungsi resol () yang ketika dijalankan dari direktori tmp pada contoh di atas, resolus ("foo") == "/ Users / greg / tmp / bar".
cd
melakukannya terlebih dahulu, sebelum meneleponpwd -P
. Dengan kata lain: itu tidak akan memungkinkan Anda untuk menyelesaikan (lihat target) symlink ke file atau dari symlink yang rusak , dan untuk menyelesaikan symlink direktori yang ada Anda harus melakukan pekerjaan tambahan (mengembalikan direktori kerja sebelumnya atau melokalkan direktori kerja sebelumnya atau memanggilcd
danpwd -P
memanggil dalam subkulit).Catatan editor: Di atas berfungsi dengan GNU
readlink
dan FreeBSD / PC-BSD / OpenBSDreadlink
, tetapi tidak pada OS X pada 10.11.GNU
readlink
menawarkan opsi tambahan yang terkait, seperti-m
untuk menyelesaikan symlink apakah ada target akhir atau tidak.Catatan sejak GNU coreutils 8.15 (2012-01-06), ada program realpath yang tersedia yang kurang tumpul dan lebih fleksibel daripada yang di atas. Ini juga kompatibel dengan util FreeBSD dengan nama yang sama. Ini juga mencakup fungsionalitas untuk menghasilkan jalur relatif antara dua file.
[Tambahan admin di bawah ini dari komentar oleh halloleo - danorton ]
Untuk Mac OS X (setidaknya melalui 10.11.x), gunakan
readlink
tanpa-f
opsi:Catatan Editor: Ini tidak akan menyelesaikan symlink secara rekursif dan karenanya tidak akan melaporkan target akhir ; misalnya, diberi symlink
a
yang menunjuk keb
, yang pada gilirannya menunjuk kec
, ini hanya akan melaporkanb
(dan tidak akan memastikan bahwa itu adalah output sebagai jalur absolut ).Gunakan
perl
perintah berikut pada OS X untuk mengisi celahreadlink -f
fungsionalitas yang hilang :perl -MCwd -le 'print Cwd::abs_path(shift)' "$path"
sumber
readlink
bekerja pada OSX, tapi perlu sintaks lain:readlink $path
tanpa itu-f
."pwd -P" tampaknya berfungsi jika Anda hanya ingin direktori, tetapi jika karena alasan tertentu Anda ingin nama executable aktual saya tidak berpikir itu membantu. Inilah solusi saya:
sumber
Salah satu favorit saya adalah
realpath foo
sumber
realpath
menggunakan Centos 6 dengan GNU coreutils 8.4.31. Saya telah menemukan beberapa orang lain di Unix & Linux yang memiliki GNU coreutils tanpa paketrealpath
. Jadi sepertinya tergantung pada lebih dari sekedar versi.realpath
lebihreadlink
karena menawarkan bendera pilihan seperti--relative-to
tampaknya persis seperti yang Anda minta - ia menerima jalur arbirary, menyelesaikan semua symlink, dan mengembalikan jalur "nyata" - dan itu "standar * nix" yang kemungkinan semua sistem sudah memiliki
sumber
Cara lain:
sumber
Menyatukan beberapa solusi yang diberikan, mengetahui bahwa readlink tersedia di sebagian besar sistem, tetapi membutuhkan argumen yang berbeda, ini berfungsi baik untuk saya di OSX dan Debian. Saya tidak yakin tentang sistem BSD. Mungkin kondisinya perlu
[[ $OSTYPE != darwin* ]]
dikecualikan-f
dari OSX saja.sumber
Berikut ini cara mendapatkan jalur sebenarnya ke file di MacOS / Unix menggunakan skrip inline Perl:
Demikian pula, untuk mendapatkan direktori file yang disinkronkan:
sumber
Apakah jalur Anda direktori, atau mungkin itu file? Jika itu direktori, sederhana saja:
Namun, jika itu mungkin file, maka ini tidak akan berfungsi:
karena symlink mungkin menyelesaikan ke jalur relatif atau penuh.
Pada skrip saya perlu menemukan path yang sebenarnya, sehingga saya dapat merujuk konfigurasi atau skrip lain yang diinstal bersama dengannya, saya menggunakan ini:
Anda dapat mengatur
SOURCE
jalur file apa pun. Pada dasarnya, selama path adalah symlink, ia menyelesaikan symlink tersebut. Triknya ada di baris terakhir loop. Jika symlink yang diselesaikan mutlak, ia akan menggunakannya sebagaiSOURCE
. Namun, jika itu relatif, itu akan menambahDIR
untuk itu, yang diselesaikan menjadi lokasi nyata dengan trik sederhana yang saya jelaskan pertama kali.sumber
sumber
Catatan: Saya percaya ini menjadi solusi yang solid, portabel, siap pakai, yang selalu panjang untuk alasan itu.
Di bawah ini adalah script / fungsi yang sepenuhnya kompatibel dengan POSIX yang karena itu lintas platform (berfungsi pada macOS juga, yang
readlink
masih belum mendukung-f
pada 10.12 (Sierra)) - ini hanya menggunakan fitur bahasa shell POSIX dan hanya panggilan utilitas yang sesuai dengan POSIX .Ini adalah implementasi portabel dari GNU
readlink -e
(versi yang lebih ketatreadlink -f
).Anda dapat menjalankan skrip dengan
sh
atau sumber yang fungsi dalambash
,ksh
danzsh
:Misalnya, di dalam skrip Anda dapat menggunakannya sebagai berikut untuk mendapatkan direktori asli skrip yang menjalankan asalnya, dengan symlink yang diselesaikan:
rreadlink
definisi skrip / fungsi:Kode diadaptasi dengan rasa terima kasih dari jawaban ini .
Saya juga membuat
bash
versi utilitas yang berdiri sendiri di sini , yang dapat Anda instalnpm install rreadlink -g
, jika Node.js telah diinstal.Garis singgung tentang keamanan:
jarno , mengacu pada fungsi yang memastikan bahwa builtin
command
tidak dibayangi oleh fungsi alias atau shell dengan nama yang sama, bertanya dalam komentar:Motivasi di balik
rreadlink
memastikan bahwacommand
memiliki makna aslinya adalah menggunakannya untuk mem-bypass (jinak) alias kenyamanan dan fungsi yang sering digunakan untuk membayangi perintah standar dalam shell interaktif, seperti mendefinisikan ulangls
untuk menyertakan opsi favorit.Saya pikir aman untuk mengatakan bahwa kecuali Anda sedang berurusan dengan, lingkungan berbahaya tidak terpercaya, mengkhawatirkan
unalias
atauunset
- atau, dalam hal ini,while
,do
, ... - yang didefinisikan ulang tidak perhatian.Ada sesuatu yang fungsi harus andalkan untuk memiliki makna dan perilaku aslinya - tidak ada jalan lain untuk itu.
Kerang seperti POSIX memungkinkan pendefinisian ulang builtin dan bahkan kata kunci bahasa secara inheren risiko keamanan (dan menulis kode paranoid sulit pada umumnya).
Untuk mengatasi masalah Anda secara khusus:
Fungsi ini bergantung pada
unalias
danunset
memiliki makna aslinya. Setelah mereka didefinisikan ulang sebagai fungsi shell dengan cara yang mengubah perilaku mereka akan menjadi masalah; redefinisi sebagai alias tidak selalu menjadi perhatian, karena mengutip (bagian dari) nama perintah (misalnya,\unalias
) mem-bypass alias.Namun, mengutip adalah bukan pilihan bagi shell kata kunci (
while
,for
,if
,do
, ...) dan sementara kata kunci shell melakukan take didahulukan atas shell fungsi , dibash
danzsh
alias memiliki hak tertinggi, sehingga untuk menjaga terhadap redefinitions shell-kata kunci Anda harus menjalankanunalias
dengan nama mereka (meskipun dalam shell non-interaktifbash
(seperti skrip) alias tidak diperluas secara default - hanya jikashopt -s expand_aliases
secara eksplisit disebut pertama).Untuk memastikan bahwa
unalias
- sebagai builtin - memiliki makna aslinya, Anda harus menggunakannya\unset
terlebih dahulu, yang mengharuskan yangunset
memiliki makna aslinya:unset
adalah shell builtin , jadi untuk memastikan bahwa itu dipanggil seperti itu, Anda harus memastikan bahwa itu sendiri tidak didefinisikan ulang sebagai suatu fungsi . Meskipun Anda bisa mem-bypass formulir alias dengan mengutip, Anda tidak bisa mem-bypass formulir fungsi shell - catch 22.Jadi, kecuali Anda dapat mengandalkan
unset
untuk memiliki makna aslinya, dari apa yang dapat saya katakan, tidak ada cara yang pasti untuk mempertahankan diri dari semua definisi-ulang berbahaya.sumber
[
sebagai alias didash
danbash
dan sebagai fungsi shell dibash
.while
sebagai fungsi dibash
,,ksh
danzsh
(tetapi tidakdash
), tetapi hanya denganfunction <name>
sintaks:function while { echo foo; }
berfungsi (while() { echo foo; }
tidak). Namun, ini tidak akan membayangiwhile
kata kunci , karena kata kunci memiliki prioritas lebih tinggi daripada fungsi (satu-satunya cara untuk memanggil fungsi ini adalah sebagai\while
). Dibash
danzsh
, alias memiliki prioritas lebih tinggi dari kata kunci, jadi alias redefinisi kata kunci memang membayangi mereka (tetapibash
secara default hanya di shell interaktif , kecuali jikashopt -s expand_aliases
secara eksplisit disebut).Script shell umum sering harus menemukan direktori "home" mereka walaupun mereka dipanggil sebagai symlink. Jadi skrip harus menemukan posisi "asli" mereka hanya dari $ 0.
pada sistem saya mencetak skrip yang berisi berikut ini, yang seharusnya menjadi petunjuk yang baik pada apa yang Anda butuhkan.
sumber
Coba ini:
sumber
Karena saya telah mengalami ini berkali-kali selama bertahun-tahun, dan kali ini saya membutuhkan versi portabel bash murni yang dapat saya gunakan di OSX dan linux, saya melanjutkan dan menulis satu:
Versi hidup tinggal di sini:
https://github.com/keen99/shell-functions/tree/master/resolve_path
tetapi demi SO, inilah versi saat ini (saya merasa sudah teruji dengan baik..tapi saya terbuka untuk umpan balik!)
Mungkin tidak sulit untuk membuatnya bekerja untuk shell bourne biasa (sh), tapi saya tidak mencoba ... Saya suka $ FUNCNAME terlalu banyak. :)
inilah contoh klasik, terima kasih untuk menyeduh:
gunakan fungsi ini dan itu akan mengembalikan jalur -real-:
sumber
Untuk mengatasi ketidakcocokan Mac, saya datang dengan
Tidak hebat selain cross OS
sumber
python -c "from os import path; print(path.realpath('${SYMLINK_PATH}'));"
mungkin akan lebih masuk akal. Namun, pada saat Anda perlu menggunakan Python dari skrip shell, Anda mungkin harus menggunakan Python dan menyelamatkan diri Anda dari sakit kepala skrip lintas platform silang.dirname
,,basename
danreadlink
merupakan utilitas eksternal , bukan shell built-in;dirname
danbasename
merupakan bagian dari POSIX,readlink
bukan.php
datang dengan OS X. Namun, meskipun tubuh pertanyaan menyebutkan OS X, itu tidak ditandai seperti itu, dan menjadi jelas bahwa orang-orang di berbagai platform datang ke sini untuk mendapatkan jawaban, jadi ada baiknya menunjukkan apa platform-spesifik / non-POSIX.Ini adalah penyelesai symlink di Bash yang berfungsi apakah tautannya adalah direktori atau bukan-direktori:
Perhatikan bahwa semua
cd
danset
hal-hal terjadi di subkulit.sumber
{}
sekitar()
tidak perlu jika tidak ada di()
belakang nama fungsi. Bash menerima deklarasi fungsi tanpa()
karena shell tidak memiliki daftar parameter dalam deklarasi dan tidak melakukan panggilan dengan()
sehingga deklarasi fungsi dengan()
tidak masuk akal.Di sini saya menyajikan apa yang saya yakini sebagai solusi lintas platform (Linux dan setidaknya MacOS) untuk jawaban yang bekerja dengan baik untuk saya saat ini.
Berikut ini adalah solusi macOS (hanya?). Mungkin lebih cocok untuk pertanyaan awal.
sumber
Jawaban saya di sini Bash: bagaimana cara mendapatkan jalur symlink yang sebenarnya?
tetapi singkatnya sangat berguna dalam skrip:
Ini adalah bagian dari GNU coreutils, cocok untuk digunakan dalam sistem Linux.
Untuk menguji semuanya, kami menaruh symlink ke / home / test2 /, mengubah beberapa hal tambahan dan menjalankan / memanggilnya dari direktori root:
Dimana
sumber
Jika pwd tidak dapat digunakan (mis. Memanggil skrip dari lokasi lain), gunakan realpath (dengan atau tanpa dirname):
Berfungsi baik saat memanggil melalui (banyak) symlink (s) atau ketika langsung memanggil skrip - dari lokasi mana pun.
sumber