Apakah itu penting jika /foo/baratau bahkan /foobenar - benar ada, atau apakah Anda hanya tertarik pada aspek manipulasi string sesuai dengan aturan nama jalur?
Chen Levy
5
@walwal ... itu agak dibuat-buat ...
Camilo Martin
2
@CamiloMartin Tidak dibikin sama sekali - itu tidak persis apa pertanyaan meminta - transformasi /foo/bar/..ke /foo, dan menggunakan bashperintah. Jika ada persyaratan lain yang tidak disebutkan, maka mungkin seharusnya ...
twalberg
14
@twalberg Anda telah melakukan terlalu banyak TDD -_- '
Camilo Martin
Jawaban:
193
jika Anda ingin mengambil bagian dari nama file dari path, "dirname" dan "basename" adalah teman Anda, dan "realpath" juga berguna.
dirname /foo/bar/baz
# /foo/bar
basename /foo/bar/baz
# baz
dirname $( dirname /foo/bar/baz )# /foo
realpath ../foo
# ../foo: No such file or directory
realpath /tmp/../tmp/../tmp
# /tmp
realpath alternatif
Jika realpathtidak didukung oleh shell Anda, Anda dapat mencoba
readlink -f /path/here/..
Juga
readlink -m /path/there/../../
Bekerja sama dengan
realpath -s /path/here/../../
karena jalan itu tidak perlu ada untuk dinormalisasi.
Bagi Anda yang membutuhkan solusi OS X, lihat jawaban Adam Liss di bawah ini.
Trenton
stackoverflow.com/a/17744637/999943 Ini adalah jawaban yang sangat terkait! Saya menemukan kedua posting QA ini dalam satu hari, dan saya ingin menghubungkan mereka bersama.
Keduanya realpathdan readlinkberasal dari utilitas inti GNU, jadi kemungkinan besar Anda mendapatkan keduanya atau tidak sama sekali. Jika saya ingat dengan benar, versi readlink pada Mac sedikit berbeda dari GNU: - \
Antoine 'hashar' Musso
100
Saya tidak tahu apakah ada perintah bash langsung untuk melakukan ini, tetapi biasanya saya lakukan
Ini akan menormalkan tetapi tidak menyelesaikan tautan lunak. Ini bisa berupa bug atau fitur. :-)
Adam Liss
4
Ini juga memiliki masalah jika $ CDPATH didefinisikan; karena "cd foo" akan beralih ke direktori "foo" yang merupakan subdirektori dari $ CDPATH, bukan hanya "foo" yang ada di direktori saat ini. Saya pikir Anda perlu melakukan sesuatu seperti: CDPATH = "" cd "$ {dirToNormalize}" && pwd -P.
mjs
7
Jawaban Tim jelas yang paling sederhana dan paling portabel. CDPATH mudah ditangani: dir = "$ (tidak disetel CDPATH && cd" $ dir "&& pwd)"
David Blevins
2
Ini bisa sangat berbahaya ( rm -rf $normalDir) jika dirToNormalisasi tidak ada!
Frank Meulenaar
4
Ya, mungkin sebaiknya menggunakan &&komentar per @ DavidBlevins.
Elias Dorneles
54
Coba realpath. Di bawah ini adalah sumber secara keseluruhan, dengan ini disumbangkan ke domain publik.
// realpath.c: display the absolute path to a file or directory.// Adam Liss, August, 2007// This program is provided "as-is" to the public domain, without express or// implied warranty, for any non-profit use, provided this notice is maintained.#include<stdio.h>#include<stdlib.h>#include<string.h>#include<libgen.h>#include<limits.h>staticchar*s_pMyName;void usage(void);int main(int argc,char*argv[]){char
sPath[PATH_MAX];
s_pMyName = strdup(basename(argv[0]));if(argc <2)
usage();
printf("%s\n", realpath(argv[1], sPath));return0;}void usage(void){
fprintf(stderr,"usage: %s PATH\n", s_pMyName);
exit(1);}
Anda harus benar-benar menyerang bagian dengan "Bonus: itu perintah bash, dan tersedia di mana-mana!" berpisah tentang realpath. Itu juga kebetulan menjadi favorit saya, tetapi alih-alih mengkompilasi alat Anda dari sumber. Ini semua untuk kelas berat, dan sebagian besar waktu yang Anda inginkan readlink -f... BTW, readlinkjuga BUKAN bash builtin, tetapi bagian dari coreutilsUbuntu.
Tomasz Gandor
4
Anda mengatakan Anda menyumbangkan ini ke domain publik, tetapi tajuknya juga mengatakan "untuk penggunaan nirlaba" - apakah Anda benar-benar bermaksud menyumbangkannya ke domain publik? Itu berarti dapat digunakan untuk tujuan nirlaba komersial maupun untuk nirlaba ...
me_and
36
Solusi portabel dan dapat diandalkan adalah dengan menggunakan python, yang sudah terinstal cukup banyak di mana-mana (termasuk Darwin). Anda memiliki dua opsi:
abspath mengembalikan jalur absolut tetapi tidak menyelesaikan symlink:
BSD tidak memiliki flag -f, yang berarti bahwa ini akan gagal bahkan pada MacOS Mojave terbaru dan banyak sistem lainnya. Lupakan menggunakan -f jika Anda ingin mudah dibawa, banyak OS yang terpengaruh.
sorin
1
@sorin. Pertanyaannya bukan tentang Mac, ini tentang Linux.
mattalxndr
14
readlinkadalah standar bash untuk mendapatkan jalur absolut. Ini juga memiliki keuntungan mengembalikan string kosong jika jalur atau jalur tidak ada (diberi bendera untuk melakukannya).
Untuk mendapatkan jalur absolut ke direktori yang mungkin atau mungkin tidak ada, tetapi siapa orang tuanya ada, gunakan:
abspath=$(readlink -f $path)
Untuk mendapatkan jalur absolut ke direktori yang harus ada bersama semua orang tua:
abspath=$(readlink -e $path)
Untuk membuat kanonikalisasi jalur yang diberikan dan mengikuti symlink jika kebetulan ada, tetapi jika tidak abaikan direktori yang hilang dan tetap kembalikan jalurnya, itu adalah:
abspath=$(readlink -m $path)
Satu-satunya downside adalah bahwa readlink akan mengikuti tautan. Jika Anda tidak ingin mengikuti tautan, Anda dapat menggunakan konvensi alternatif ini:
abspath=$(cd ${path%/*}&& echo $PWD/${path##*/})
Itu akan chdir ke bagian direktori $ path dan mencetak direktori saat ini bersama dengan bagian file $ path. Jika gagal chdir, Anda mendapatkan string kosong dan kesalahan pada stderr.
readlinkadalah pilihan yang baik jika tersedia. Versi OS X tidak mendukung opsi -eatau -f. Dalam tiga contoh pertama, Anda harus memiliki tanda kutip ganda $pathuntuk menangani spasi atau wildcard dalam nama file. +1 untuk ekspansi parameter, tetapi ini memiliki kerentanan keamanan. Jika pathkosong, ini akan cdke direktori home Anda. Anda perlu tanda kutip ganda. abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
toxalot
Ini hanya sebuah contoh. Jika Anda benar-benar mengandalkan keamanan, maka Anda benar-benar tidak boleh menggunakan bash atau varian shell lainnya sama sekali. Juga, bash memiliki masalah sendiri dalam hal kompatibilitas lintas platform, serta memiliki masalah dengan perubahan fungsionalitas antara versi utama. OSX hanyalah salah satu dari banyak platform dengan masalah yang berkaitan dengan skrip shell, belum lagi didasarkan pada BSD. Ketika Anda harus benar-benar multi platform, Anda harus mematuhi POSIX, jadi ekspansi parameter benar-benar keluar dari jendela. Lihatlah Solaris atau HP-UX beberapa saat.
Craig
6
Tidak ada maksud pelanggaran di sini, tetapi menunjukkan masalah yang tidak jelas seperti ini adalah penting. Saya hanya ingin jawaban cepat untuk masalah sepele ini dan saya akan percaya kode itu dengan semua / semua masukan jika bukan karena komentar di atas. Penting juga untuk mendukung OS-X dalam diskusi bash ini. Ada banyak perintah yang sayangnya tidak didukung pada OS-X, dan banyak forum menerima begitu saja ketika membahas Bash yang berarti kita akan terus mendapatkan banyak masalah lintas-platform kecuali itu ditangani lebih cepat daripada nanti.
Rebs
13
Pertanyaan lama, tetapi ada cara yang lebih sederhana jika Anda berurusan dengan nama path lengkap di level shell:
abspath = "$ (cd" $ path "&& pwd)"
Ketika cd terjadi dalam sebuah subshell itu tidak mempengaruhi skrip utama.
Dua variasi, seandainya perintah bawaan shell Anda menerima -L dan -P, adalah:
abspath = "$ (cd -P" $ path "&& pwd -P)" # path fisik dengan symlink yang diselesaikan
abspath = "$ (cd -L" $ path "&& pwd -L)" # jalur logis yang mempertahankan symlink
Secara pribadi, saya jarang membutuhkan pendekatan nanti kecuali saya terpesona dengan tautan simbolis untuk beberapa alasan.
FYI: variasi untuk mendapatkan direktori awal skrip yang berfungsi meskipun skrip berubah direktori saat ini di kemudian hari.
name0 = "$ (basename" $ 0 ")"; #basis nama skrip
dir0 = "$ (cd" $ (dirname "$ 0") "&& pwd)"; #memulai direct dir
Penggunaan CD memastikan Anda selalu memiliki direktori absolut, bahkan jika skrip dijalankan oleh perintah seperti ./script.sh yang, tanpa cd / pwd, sering memberikan hanya .. Tidak berguna jika skrip melakukan cd nanti.
Seperti yang dikatakan Adam Liss realpathtidak dibundel dengan setiap distribusi. Yang memalukan, karena itu adalah solusi terbaik. Kode sumber yang disediakan sangat bagus, dan saya mungkin akan mulai menggunakannya sekarang. Inilah yang saya gunakan sampai sekarang, yang saya bagikan di sini hanya untuk kelengkapan:
get_abs_path(){local PARENT_DIR=$(dirname "$1")
cd "$PARENT_DIR"local ABS_PATH="$(pwd)"/"$(basename "$1")"
cd ->/dev/null
echo "$ABS_PATH"}
Jika Anda ingin menyelesaikan symlink, ganti saja pwddengan pwd -P.
Satu gotcha dengan pwd -Popsi untuk kasus ini ... Pertimbangkan apa yang akan terjadi jika $(basename "$1")symlink ke file di direktori lain. Satu- pwd -Psatunya menyelesaikan symlink di bagian direktori path, tetapi tidak bagian basename.
Saya menduga ini gagal jika argumennya bukan direktori. Misalkan saya ingin tahu ke mana / usr / bin / java mengarah ke?
Edward Falk
1
Jika Anda tahu itu file, Anda bisa pushd $(dirname /usr/bin/java)mencobanya.
schmunk
5
Bukan jawaban yang tepat tetapi mungkin pertanyaan lanjutan (pertanyaan awal tidak eksplisit):
readlinktidak apa-apa jika Anda benar-benar ingin mengikuti symlink. Tetapi ada juga kasus penggunaan hanya untuk menormalkan ./dan ../dan //urutan, yang dapat dilakukan secara sintaksis murni, tanpa mengaitkan symlinks. readlinktidak baik untuk ini, dan juga tidak realpath.
for f in $paths;do(cd $f; pwd);done
bekerja untuk jalur yang ada, tetapi istirahat untuk orang lain.
Sebuah sedskrip tampaknya menjadi taruhan yang bagus, kecuali bahwa Anda tidak dapat secara iteratif mengganti urutan ( /foo/bar/baz/../..-> /foo/bar/..-> /foo) tanpa menggunakan sesuatu seperti Perl, yang tidak aman untuk diasumsikan pada semua sistem, atau menggunakan beberapa loop jelek untuk membandingkan output dari sedke inputnya.
FWIW, one-liner menggunakan Java (JDK 6+):
jrunscript -e 'for (var i = 0; i < arguments.length; i++) {println(new java.io.File(new java.io.File(arguments[i]).toURI().normalize()))}' $paths
realpathmemiliki -sopsi untuk tidak menyelesaikan tautan simbolik dan hanya menyelesaikan referensi /./, /../dan menghapus /karakter tambahan . Ketika dikombinasikan dengan -mopsi, realpathhanya beroperasi pada nama file, dan tidak menyentuh file yang sebenarnya. Ini terdengar seperti solusi yang sempurna. Namun sayang, realpathmasih banyak yang hilang pada sistem.
toxalot
Menghapus ..komponen tidak dapat dilakukan secara sintaksis ketika symlinks terlibat. /one/two/../threetidak sama dengan /one/threejika twosymlink ke /foo/bar.
jrw32982 mendukung Monica
@ jrw32982 ya seperti yang saya katakan dalam tanggapan saya ini untuk kasus penggunaan ketika kanonikisasi symlink tidak diinginkan atau dibutuhkan.
Jesse Glick
@ JesseGlick itu bukan hanya masalah apakah Anda ingin mengkanoniskan symlink atau tidak. Algoritme Anda sebenarnya menghasilkan jawaban yang salah. Agar jawaban Anda benar, Anda harus tahu apriori bahwa tidak ada symlink yang terlibat (atau mereka hanya dari bentuk tertentu). Jawaban Anda mengatakan bahwa Anda tidak ingin mengkanoniskannya, bukan karena tidak ada symlink yang terlibat di jalur.
jrw32982 mendukung Monica
Ada kasus penggunaan di mana normalisasi harus dilakukan tanpa mengasumsikan struktur direktori yang sudah ada dan sudah diperbaiki. Normalisasi URI serupa. Dalam kasus ini, ini adalah batasan yang melekat bahwa hasilnya umumnya tidak akan benar jika kebetulan ada symlink di dekat direktori di mana hasilnya nanti diterapkan.
Jesse Glick
5
Saya terlambat ke pesta, tapi ini solusi yang saya buat setelah membaca banyak utas seperti ini:
resolve_dir(){(builtin cd `dirname "${1/#~/$HOME}"`'/'`basename "${1/#~/$HOME}"`2>/dev/null;if[ $?-eq 0];then pwd;fi)}
Ini akan menyelesaikan jalur absolut $ 1, bermain bagus dengan ~, menjaga symlink di jalur tempat mereka berada, dan itu tidak akan mengacaukan dengan tumpukan direktori Anda. Ini mengembalikan jalur penuh atau tidak sama sekali jika tidak ada. Ia mengharapkan $ 1 menjadi direktori dan mungkin akan gagal jika tidak, tapi itu pemeriksaan yang mudah untuk dilakukan sendiri.
Banyak bicara, dan agak terlambat menjawab. Saya perlu menulis satu karena saya terjebak pada RHEL4 / 5 yang lebih tua. Saya menangani tautan absolut dan relatif, dan menyederhanakan entri //, /./ dan somedir /../.
Coba produk perpustakaan Bash baru kami realpath-lib yang telah kami tempatkan di GitHub secara gratis dan tidak terbebani. Ini sepenuhnya didokumentasikan dan menjadi alat pembelajaran yang hebat.
Ini menyelesaikan jalur lokal, relatif dan absolut dan tidak memiliki dependensi kecuali Bash 4+; jadi itu harus bekerja di mana saja. Gratis, bersih, sederhana, dan instruktif.
Masalahnya realpathadalah bahwa itu tidak tersedia pada BSD (atau OSX dalam hal ini). Ini adalah resep sederhana yang diambil dari artikel yang agak lama (2009) dari Linux Journal , yang cukup portabel:
function normpath(){# Remove all /./ sequences.local path=${1//\/.\//\/}# Remove dir/.. sequences.while[[ $path =~([^/][^/]*/\.\./)]];do
path=${path/${BASH_REMATCH[0]}/}done
echo $path
}
Perhatikan varian ini juga tidak membutuhkan jalur untuk ada.
Namun ini tidak menyelesaikan symlink. Realpath memproses jalur yang dimulai pada root dan mengikuti symlink saat proses berlangsung. Semua ini dilakukan adalah runtuh referensi orang tua.
Martijn Pieters
2
Berdasarkan jawaban @ Andre, saya mungkin memiliki versi yang sedikit lebih baik, kalau-kalau ada seseorang yang mencari solusi berbasis manipulasi string yang bebas loop. Ini juga berguna bagi mereka yang tidak ingin melakukan dereferensi symlink, yang merupakan kelemahan dari penggunaan realpathatau readlink -f.
Ini bekerja pada versi bash 3.2.25 dan lebih tinggi.
shopt -s extglob
normalise_path(){local path="$1"# get rid of /../ example: /one/../two to /two
path="${path//\/*([!\/])\/\.\./}"# get rid of /./ and //* example: /one/.///two to /one/two
path="${path//@(\/\.\/|\/+(\/))//}"# remove the last '/.'
echo "${path%%/.}"}
$ normalise_path /home/codemedic/../codemedic////.config
/home/codemedic/.config
Ini adalah ide yang bagus, tetapi saya menyia-nyiakan waktu 20 menit untuk membuatnya bekerja pada berbagai versi bash yang berbeda. Ternyata opsi shell extglob perlu dihidupkan agar ini berfungsi, dan itu tidak secara default. Ketika datang ke fungsi bash, penting untuk menentukan versi yang diperlukan dan opsi non-default karena detail ini dapat bervariasi di antara OS. Sebagai contoh, versi terbaru dari Mac OSX (Yosemite) hanya hadir dengan versi bash yang lama (3.2).
drwatsoncode
Maaf @ricovox; Saya sekarang telah memperbarui itu. Saya ingin mengetahui versi persis Bash yang Anda miliki di sana. Rumus di atas (diperbarui) bekerja pada CentOS 5.8 yang dilengkapi dengan bash 3.2.25
12οδεMεδιϲ
Maaf bila membingungkan. Kode ini DID berfungsi pada versi Mac OSX saya bash (3.2.57) setelah saya mengaktifkan ekstglob. Catatan saya tentang versi bash lebih umum (yang sebenarnya lebih banyak berlaku untuk jawaban lain di sini mengenai regex di bash).
drwatsoncode
2
Saya menghargai jawaban Anda. Saya menggunakannya sebagai basis untuk saya sendiri. Kebetulan saya perhatikan beberapa kasus di mana milik Anda gagal: (1) Jalur relatif hello/../world(2) Titik dalam nama file /hello/..world(3) Titik setelah tebasan ganda (4) Titik /hello//../worldsebelum atau sesudah tebasan ganda /hello//./worldatau /hello/.//world (5) Induk setelah saat ini: /hello/./../world/ (6) Induk setelah Induk:, /hello/../../worlddll. - Beberapa di antaranya dapat diperbaiki dengan menggunakan loop untuk melakukan koreksi sampai jalur berhenti berubah. (Hapus juga dir/../, /dir/..tapi hapus dir/..dari akhir.)
drwatsoncode
0
Berdasarkan potongan python loveborg yang sangat baik, saya menulis ini:
#!/bin/sh# Version of readlink that follows links to the end; good for Mac OS Xfor file in"$@";dowhile[-h "$file"];do
l=`readlink $file`case"$l"in/*) file="$l";;*) file=`dirname "$file"`/"$l"esacdone#echo $file
python -c "import os,sys; print os.path.abspath(sys.argv[1])""$file"done
Berikut ini adalah test case pendek dengan beberapa ruang bengkok di jalur untuk sepenuhnya menggunakan kutipan
mkdir -p "/tmp/test/ first path "
mkdir -p "/tmp/test/ second path "
echo "hello">"/tmp/test/ first path / red .txt "
ln -s "/tmp/test/ first path / red .txt ""/tmp/test/ second path / green .txt "
cd "/tmp/test/ second path "
fullpath " green .txt "
cat " green .txt "
Saya tahu ini adalah pertanyaan kuno. Saya masih menawarkan alternatif. Baru-baru ini saya bertemu dengan masalah yang sama dan tidak menemukan perintah yang ada dan portabel untuk melakukan itu. Jadi saya menulis skrip shell berikut yang mencakup fungsi yang dapat melakukan trik.
#! /bin/sh function normalize {local rc=0local ret
if[ $# -gt 0 ] ; then# invalidif["x`echo $1 | grep -E '^/\.\.'`"!="x"];then
echo $1
return-1fi# convert to absolute pathif["x`echo $1 | grep -E '^\/'`"=="x"];then
normalize "`pwd`/$1"return $?fi
ret=`echo $1 | sed 's;/\.\($\|/\);/;g' | sed 's;/[^/]*[^/.]\+[^/]*/\.\.\($\|/\);/;g'`else
read line
normalize "$line"return $?fiif["x`echo $ret | grep -E '/\.\.?(/|$)'`"!="x"];then
ret=`normalize "$ret"`
rc=$?fi
echo "$ret"return $rc
}
Saya membuat fungsi builtin-only untuk menangani ini dengan fokus pada kinerja setinggi mungkin (untuk bersenang-senang). Itu tidak menyelesaikan symlink, jadi pada dasarnya sama dengan realpath -sm.
## A bash-only mimic of `realpath -sm`. ## Give it path[s] as argument[s] and it will convert them to clean absolute paths
abspath (){
${*+false}&&{>&2 echo $FUNCNAME: missing operand;return1;};local c s p IFS='/';## path chunk, absolute path, input path, IFS for splitting paths into chunkslocal-i r=0;## return valuefor p in"$@";docase"$p"in## Check for leading backslashes, identify relative/absolute path'')((r|=1));continue;;//[!/]*)>&2 echo "paths =~ ^//[^/]* are impl-defined; not my problem";((r|=2));continue;;/*);;*) p="$PWD/$p";;## Prepend the current directory to form an absolute pathesac
s='';for c in $p;do## Let IFS split the path at '/'scase $c in### NOTE: IFS is '/'; so no quotes needed here''|.);;## Skip duplicate '/'s and '/./'s..) s="${s%/*}";;## Trim the previous addition to the absolute path string*) s+=/$c;;### NOTE: No quotes here intentionally. They make no difference, it seemsesac;done;
echo "${s:-/}";## If xpg_echo is set, use `echo -E` or `printf $'%s\n'` insteaddonereturn $r;}
Catatan: Fungsi ini tidak menangani lintasan yang dimulai dengan //, karena tepat dua garis miring ganda pada awal lintasan adalah perilaku yang ditentukan implementasi. Namun, menangani /, ///dan sebagainya baik-baik saja.
Fungsi ini tampaknya menangani semua kasus tepi dengan benar, tetapi mungkin masih ada beberapa di luar sana yang belum saya tangani.
Catatan Kinerja: ketika dipanggil dengan ribuan argumen, abspathberjalan sekitar 10x lebih lambat dari realpath -sm; ketika dipanggil dengan satu argumen,abspath berjalan> 110x lebih cepat dari realpath -smpada mesin saya, sebagian besar karena tidak perlu menjalankan program baru setiap kali.
yang stat -f %N ~/Documentsgaris adalah ikan merah ... shell Anda mengganti ~/Documentsdengan /Users/me/Documents, dan stathanya mencetak argumen verbatim.
/foo/bar
atau bahkan/foo
benar - benar ada, atau apakah Anda hanya tertarik pada aspek manipulasi string sesuai dengan aturan nama jalur?/foo/bar/..
ke/foo
, dan menggunakanbash
perintah. Jika ada persyaratan lain yang tidak disebutkan, maka mungkin seharusnya ...Jawaban:
jika Anda ingin mengambil bagian dari nama file dari path, "dirname" dan "basename" adalah teman Anda, dan "realpath" juga berguna.
realpath
alternatifJika
realpath
tidak didukung oleh shell Anda, Anda dapat mencobaJuga
Bekerja sama dengan
karena jalan itu tidak perlu ada untuk dinormalisasi.
sumber
realpath
danreadlink
berasal dari utilitas inti GNU, jadi kemungkinan besar Anda mendapatkan keduanya atau tidak sama sekali. Jika saya ingat dengan benar, versi readlink pada Mac sedikit berbeda dari GNU: - \Saya tidak tahu apakah ada perintah bash langsung untuk melakukan ini, tetapi biasanya saya lakukan
dan itu bekerja dengan baik.
sumber
rm -rf $normalDir
) jika dirToNormalisasi tidak ada!&&
komentar per @ DavidBlevins.Coba
realpath
. Di bawah ini adalah sumber secara keseluruhan, dengan ini disumbangkan ke domain publik.sumber
realpath
. Itu juga kebetulan menjadi favorit saya, tetapi alih-alih mengkompilasi alat Anda dari sumber. Ini semua untuk kelas berat, dan sebagian besar waktu yang Anda inginkanreadlink -f
... BTW,readlink
juga BUKAN bash builtin, tetapi bagian daricoreutils
Ubuntu.Solusi portabel dan dapat diandalkan adalah dengan menggunakan python, yang sudah terinstal cukup banyak di mana-mana (termasuk Darwin). Anda memiliki dua opsi:
abspath
mengembalikan jalur absolut tetapi tidak menyelesaikan symlink:python -c "import os,sys; print(os.path.abspath(sys.argv[1]))" path/to/file
realpath
mengembalikan jalur absolut dan dengan itu menyelesaikan symlink, menghasilkan jalur kanonik:python -c "import os,sys; print(os.path.realpath(sys.argv[1]))" path/to/file
Dalam setiap kasus,
path/to/file
dapat berupa jalur relatif atau absolut.sumber
readlink
tersedia di OS X, hanya saja tidak dengan-f
opsi. Solusi portabel dibahas di sini .Gunakan utilitas readlink dari paket coreutils.
sumber
readlink
adalah standar bash untuk mendapatkan jalur absolut. Ini juga memiliki keuntungan mengembalikan string kosong jika jalur atau jalur tidak ada (diberi bendera untuk melakukannya).Untuk mendapatkan jalur absolut ke direktori yang mungkin atau mungkin tidak ada, tetapi siapa orang tuanya ada, gunakan:
Untuk mendapatkan jalur absolut ke direktori yang harus ada bersama semua orang tua:
Untuk membuat kanonikalisasi jalur yang diberikan dan mengikuti symlink jika kebetulan ada, tetapi jika tidak abaikan direktori yang hilang dan tetap kembalikan jalurnya, itu adalah:
Satu-satunya downside adalah bahwa readlink akan mengikuti tautan. Jika Anda tidak ingin mengikuti tautan, Anda dapat menggunakan konvensi alternatif ini:
Itu akan chdir ke bagian direktori $ path dan mencetak direktori saat ini bersama dengan bagian file $ path. Jika gagal chdir, Anda mendapatkan string kosong dan kesalahan pada stderr.
sumber
readlink
adalah pilihan yang baik jika tersedia. Versi OS X tidak mendukung opsi-e
atau-f
. Dalam tiga contoh pertama, Anda harus memiliki tanda kutip ganda$path
untuk menangani spasi atau wildcard dalam nama file. +1 untuk ekspansi parameter, tetapi ini memiliki kerentanan keamanan. Jikapath
kosong, ini akancd
ke direktori home Anda. Anda perlu tanda kutip ganda.abspath=$(cd "${path%/*}" && echo "$PWD/${path##*/}")
Pertanyaan lama, tetapi ada cara yang lebih sederhana jika Anda berurusan dengan nama path lengkap di level shell:
Ketika cd terjadi dalam sebuah subshell itu tidak mempengaruhi skrip utama.
Dua variasi, seandainya perintah bawaan shell Anda menerima -L dan -P, adalah:
Secara pribadi, saya jarang membutuhkan pendekatan nanti kecuali saya terpesona dengan tautan simbolis untuk beberapa alasan.
FYI: variasi untuk mendapatkan direktori awal skrip yang berfungsi meskipun skrip berubah direktori saat ini di kemudian hari.
Penggunaan CD memastikan Anda selalu memiliki direktori absolut, bahkan jika skrip dijalankan oleh perintah seperti ./script.sh yang, tanpa cd / pwd, sering memberikan hanya .. Tidak berguna jika skrip melakukan cd nanti.
sumber
Seperti yang dikatakan Adam Liss
realpath
tidak dibundel dengan setiap distribusi. Yang memalukan, karena itu adalah solusi terbaik. Kode sumber yang disediakan sangat bagus, dan saya mungkin akan mulai menggunakannya sekarang. Inilah yang saya gunakan sampai sekarang, yang saya bagikan di sini hanya untuk kelengkapan:Jika Anda ingin menyelesaikan symlink, ganti saja
pwd
denganpwd -P
.sumber
pwd -P
opsi untuk kasus ini ... Pertimbangkan apa yang akan terjadi jika$(basename "$1")
symlink ke file di direktori lain. Satu-pwd -P
satunya menyelesaikan symlink di bagian direktori path, tetapi tidak bagian basename.Solusi terakhir saya adalah:
Berdasarkan jawaban Tim Whitcomb.
sumber
pushd $(dirname /usr/bin/java)
mencobanya.Bukan jawaban yang tepat tetapi mungkin pertanyaan lanjutan (pertanyaan awal tidak eksplisit):
readlink
tidak apa-apa jika Anda benar-benar ingin mengikuti symlink. Tetapi ada juga kasus penggunaan hanya untuk menormalkan./
dan../
dan//
urutan, yang dapat dilakukan secara sintaksis murni, tanpa mengaitkan symlinks.readlink
tidak baik untuk ini, dan juga tidakrealpath
.bekerja untuk jalur yang ada, tetapi istirahat untuk orang lain.
Sebuah
sed
skrip tampaknya menjadi taruhan yang bagus, kecuali bahwa Anda tidak dapat secara iteratif mengganti urutan (/foo/bar/baz/../..
->/foo/bar/..
->/foo
) tanpa menggunakan sesuatu seperti Perl, yang tidak aman untuk diasumsikan pada semua sistem, atau menggunakan beberapa loop jelek untuk membandingkan output darised
ke inputnya.FWIW, one-liner menggunakan Java (JDK 6+):
sumber
realpath
memiliki-s
opsi untuk tidak menyelesaikan tautan simbolik dan hanya menyelesaikan referensi/./
,/../
dan menghapus/
karakter tambahan . Ketika dikombinasikan dengan-m
opsi,realpath
hanya beroperasi pada nama file, dan tidak menyentuh file yang sebenarnya. Ini terdengar seperti solusi yang sempurna. Namun sayang,realpath
masih banyak yang hilang pada sistem...
komponen tidak dapat dilakukan secara sintaksis ketika symlinks terlibat./one/two/../three
tidak sama dengan/one/three
jikatwo
symlink ke/foo/bar
.Saya terlambat ke pesta, tapi ini solusi yang saya buat setelah membaca banyak utas seperti ini:
Ini akan menyelesaikan jalur absolut $ 1, bermain bagus dengan ~, menjaga symlink di jalur tempat mereka berada, dan itu tidak akan mengacaukan dengan tumpukan direktori Anda. Ini mengembalikan jalur penuh atau tidak sama sekali jika tidak ada. Ia mengharapkan $ 1 menjadi direktori dan mungkin akan gagal jika tidak, tapi itu pemeriksaan yang mudah untuk dilakukan sendiri.
sumber
Banyak bicara, dan agak terlambat menjawab. Saya perlu menulis satu karena saya terjebak pada RHEL4 / 5 yang lebih tua. Saya menangani tautan absolut dan relatif, dan menyederhanakan entri //, /./ dan somedir /../.
sumber
Coba produk perpustakaan Bash baru kami realpath-lib yang telah kami tempatkan di GitHub secara gratis dan tidak terbebani. Ini sepenuhnya didokumentasikan dan menjadi alat pembelajaran yang hebat.
Ini menyelesaikan jalur lokal, relatif dan absolut dan tidak memiliki dependensi kecuali Bash 4+; jadi itu harus bekerja di mana saja. Gratis, bersih, sederhana, dan instruktif.
Anda dapat melakukan:
Fungsi ini adalah inti dari perpustakaan:
Ini juga berisi fungsi untuk get_dirname, get_filename, get_ stemname dan validate_path. Cobalah di seluruh platform, dan bantu untuk memperbaikinya.
sumber
Masalahnya
realpath
adalah bahwa itu tidak tersedia pada BSD (atau OSX dalam hal ini). Ini adalah resep sederhana yang diambil dari artikel yang agak lama (2009) dari Linux Journal , yang cukup portabel:Perhatikan varian ini juga tidak membutuhkan jalur untuk ada.
sumber
Berdasarkan jawaban @ Andre, saya mungkin memiliki versi yang sedikit lebih baik, kalau-kalau ada seseorang yang mencari solusi berbasis manipulasi string yang bebas loop. Ini juga berguna bagi mereka yang tidak ingin melakukan dereferensi symlink, yang merupakan kelemahan dari penggunaan
realpath
ataureadlink -f
.Ini bekerja pada versi bash 3.2.25 dan lebih tinggi.
sumber
hello/../world
(2) Titik dalam nama file/hello/..world
(3) Titik setelah tebasan ganda (4) Titik/hello//../world
sebelum atau sesudah tebasan ganda/hello//./world
atau/hello/.//world
(5) Induk setelah saat ini:/hello/./../world/
(6) Induk setelah Induk:,/hello/../../world
dll. - Beberapa di antaranya dapat diperbaiki dengan menggunakan loop untuk melakukan koreksi sampai jalur berhenti berubah. (Hapus jugadir/../
,/dir/..
tapi hapusdir/..
dari akhir.)Berdasarkan potongan python loveborg yang sangat baik, saya menulis ini:
sumber
Ini berfungsi bahkan jika file tidak ada. Itu memang membutuhkan direktori yang berisi file untuk ada.
sumber
realpath -e
Saya membutuhkan solusi yang akan melakukan ketiganya:
realpath
danreadlink -f
addonsTidak ada jawaban yang memiliki # 1 dan # 2. Saya menambahkan # 3 untuk menyelamatkan orang lain dari bercukur yak lebih lanjut.
Berikut ini adalah test case pendek dengan beberapa ruang bengkok di jalur untuk sepenuhnya menggunakan kutipan
sumber
Saya tahu ini adalah pertanyaan kuno. Saya masih menawarkan alternatif. Baru-baru ini saya bertemu dengan masalah yang sama dan tidak menemukan perintah yang ada dan portabel untuk melakukan itu. Jadi saya menulis skrip shell berikut yang mencakup fungsi yang dapat melakukan trik.
https://gist.github.com/bestofsong/8830bdf3e5eb9461d27313c3c282868c
sumber
Saya membuat fungsi builtin-only untuk menangani ini dengan fokus pada kinerja setinggi mungkin (untuk bersenang-senang). Itu tidak menyelesaikan symlink, jadi pada dasarnya sama dengan
realpath -sm
.Catatan: Fungsi ini tidak menangani lintasan yang dimulai dengan
//
, karena tepat dua garis miring ganda pada awal lintasan adalah perilaku yang ditentukan implementasi. Namun, menangani/
,///
dan sebagainya baik-baik saja.Fungsi ini tampaknya menangani semua kasus tepi dengan benar, tetapi mungkin masih ada beberapa di luar sana yang belum saya tangani.
Catatan Kinerja: ketika dipanggil dengan ribuan argumen,
abspath
berjalan sekitar 10x lebih lambat darirealpath -sm
; ketika dipanggil dengan satu argumen,abspath
berjalan> 110x lebih cepat darirealpath -sm
pada mesin saya, sebagian besar karena tidak perlu menjalankan program baru setiap kali.sumber
Saya menemukan hari ini bahwa Anda dapat menggunakan
stat
perintah untuk menyelesaikan jalur.Jadi untuk direktori seperti "~ / Documents":
Anda dapat menjalankan ini:
stat -f %N ~/Documents
Untuk mendapatkan path lengkap:
/Users/me/Documents
Untuk symlink, Anda dapat menggunakan opsi format% Y:
stat -f %Y example_symlink
Yang mungkin mengembalikan hasil seperti:
/usr/local/sbin/example_symlink
Opsi pemformatan mungkin berbeda pada versi lain * NIX tetapi ini bekerja untuk saya di OSX.
sumber
stat -f %N ~/Documents
garis adalah ikan merah ... shell Anda mengganti~/Documents
dengan/Users/me/Documents
, danstat
hanya mencetak argumen verbatim.Solusi sederhana menggunakan
node.js
:sumber