Bagaimana cara mendapatkan lintasan direktori tempat skrip Bash berada, di dalam skrip itu?
Saya ingin menggunakan skrip Bash sebagai peluncur untuk aplikasi lain. Saya ingin mengubah direktori kerja ke direktori tempat skrip Bash berada, sehingga saya dapat beroperasi pada file di direktori itu, seperti:
$ ./application
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd && echo x)"
- dan menghapusnya tanpa substitusi perintah -DIR="${DIR%x}"
.mkdir $'\n'
.dirname
, dan bahwa direktori dapat dimulai dengan-
(misalnya--help
).DIR=$(reldir=$(dirname -- "$0"; echo x); reldir=${reldir%?x}; cd -- "$reldir" && pwd && echo x); DIR=${DIR%?x}
. Mungkin ini berlebihan?Jawaban:
adalah satu-liner yang bermanfaat yang akan memberi Anda nama direktori lengkap dari skrip di mana pun ia dipanggil.
Ini akan berfungsi selama komponen terakhir dari jalur yang digunakan untuk menemukan skrip bukan symlink (tautan direktori OK). Jika Anda juga ingin menyelesaikan tautan apa pun ke skrip itu sendiri, Anda memerlukan solusi multisaluran:
Yang terakhir ini akan bekerja dengan kombinasi alias,
source
,bash -c
, symlink, dllHati-hati: jika Anda
cd
ke direktori yang berbeda sebelum menjalankan cuplikan ini, hasilnya mungkin salah!Selain itu, perhatikan
$CDPATH
gotchas , dan efek samping keluaran stderr jika pengguna mengganti cd dengan cerdas untuk mengalihkan output ke stderr (termasuk urutan keluar, seperti saat memanggilupdate_terminal_cwd >&2
Mac). Menambahkan>/dev/null 2>&1
pada akhircd
perintah Anda akan menangani kedua kemungkinan.Untuk memahami cara kerjanya, coba jalankan formulir yang lebih bertele-tele ini:
Dan itu akan mencetak sesuatu seperti:
sumber
source <script>
danbash <script>
:DIR="$(cd -P "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
.cd
mencetak sesuatu ke STDOUT! Misal, jika$CDPATH
sudah.
. Untuk membahas kasus ini, gunakanDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null && pwd )"
dirname $(readlink -f $0)
adalah perintah yang tepat. Lihat gist.github.com/tvlooy/cbfbdb111a4ebad8b93e untuk testcasedirname "$(readlink -f "$0")"
tidak menambah kerumitan dan ukuran yang adil lebih kuat untuk jumlah masalah minimal.readlink -f $0
memberireadlink: illegal option -- f
.Gunakan
dirname "$0"
:menggunakan
pwd
sendiri tidak akan berfungsi jika Anda tidak menjalankan skrip dari direktori yang ada di dalamnya.sumber
type -p
jika skrip dapat dieksekusi. Ini juga dapat membuka lubang halus jika skrip dieksekusi menggunakanbash test2.sh
dan ada skrip lain dengan nama yang sama dieksekusi di tempat lain.bash
dan hash-bang line secara eksplisit menyebutkan/bin/bash
saya akan mengatakan itu cukup aman untuk bergantung pada bashism.dirname $0
adalah jika direktori tersebut adalah direktori saat ini, Anda akan mendapatkannya.
. Tidak apa-apa kecuali Anda akan mengubah direktori dalam skrip dan berharap untuk menggunakan jalur yang Anda dapatkandirname $0
seolah-olah itu mutlak. Untuk mendapatkan path absolut:pushd `dirname $0` > /dev/null
,SCRIPTPATH=`pwd`
,popd > /dev/null
: pastie.org/1489386 (Tapi pasti ada cara yang lebih baik untuk memperluas jalan itu?)dirname $0
masalah jika Anda menetapkannya ke variabel dan kemudian menggunakannya untuk meluncurkan skrip seperti$dir/script.sh
; Saya akan membayangkan ini adalah kasus penggunaan untuk hal semacam ini 90% dari waktu../script.sh
akan bekerja dengan baik.The
dirname
perintah yang paling dasar, hanya parsing jalan sampai ke nama file off dari$0
variabel (nama script):Tetapi, seperti ditunjukkan matt b , path yang dikembalikan berbeda tergantung pada bagaimana script dipanggil.
pwd
tidak melakukan pekerjaan karena itu hanya memberi tahu Anda apa direktori saat ini, bukan direktori tempat skrip berada. Selain itu, jika tautan simbolis ke skrip dijalankan, Anda akan mendapatkan jalur (mungkin relatif) untuk tempat tautan berada, bukan skrip yang sebenarnya.Beberapa orang lain telah menyebutkan
readlink
perintah, tetapi yang paling sederhana, Anda dapat menggunakan:readlink
akan menyelesaikan jalur skrip ke jalur absolut dari root filesystem. Jadi, setiap jalur yang mengandung titik tunggal atau ganda, tilde dan / atau tautan simbolik akan diselesaikan ke jalur penuh.Berikut adalah script menunjukkan masing-masing,
whatdir.sh
:Menjalankan skrip ini di direktori home saya, menggunakan jalur relatif:
Sekali lagi, tetapi menggunakan path lengkap ke skrip:
Sekarang mengubah direktori:
Dan akhirnya menggunakan tautan simbolis untuk menjalankan skrip:
sumber
readlink
tidak akan tersedia di beberapa platform dalam instalasi default. Cobalah untuk menghindari menggunakannya jika Anda bisaexport SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
-f
tidak dikenali sebagai opsi untukreadlink
. Menggunakanstat -f
bukan melakukan pekerjaan. Terima kasihgreadlink
, yang pada dasarnyareadlink
kita semua kenal. Ini adalah versi platform independen:dir=`greadlink -f ${BASH_SOURCE[0]} || readlink -f ${BASH_SOURCE[0]}`
greadlink
dapat dengan mudah diinstal melalui homebrew:brew install coreutils
Bekerja untuk semua versi, termasuk
source
" alias.
operator (titik).$0
dimodifikasi dari pemanggil."./script"
"/full/path/to/script"
"/some/path/../../another/path/script"
"./some/folder/script"
Atau, jika skrip bash itu sendiri adalah symlink relatif yang ingin Anda ikuti dan kembalikan path lengkap skrip ditautkan ke:
SCRIPT_PATH
diberikan dalam jalur penuh, tidak peduli bagaimana itu disebut.Pastikan Anda menemukan ini di awal skrip.
Ini komentar dan kode Copyleft, lisensi yang dipilih di bawah GPL2.0 atau lebih baru atau CC-SA 3.0 (CreativeCommons Share Alike) atau lebih baru. (c) 2008. Hak cipta dilindungi undang-undang. Tidak ada jaminan dalam bentuk apa pun. Anda telah diperingatkan.
http://www.gnu.org/licenses/gpl-2.0.txt
http://creativecommons.org/licenses/by-sa/3.0/
18eedfe1c99df68dc94d4a94712a71aaa8e1e9e36cacf421b9463dd2bbaa02906d0d6656
sumber
readlink -f $(dirname "${VIRTUAL_ENV}")
;dirname "${SCRIPT_PATH}"
&& pwd)? Tapi bagaimanapun juga skrip yang bagus!cd
keluar dari direktori saat ini dengan harapancd
untuk kembali lagi nanti: Skrip mungkin tidak memiliki izin untuk mengubah direktori kembali ke direktori yang aktif saat itu dipanggil. (Sama berlaku untuk pushd / popd)readlink -f
khusus untuk GNU. BSDreadlink
tidak memiliki opsi itu.([ ... ])
kurang efisien daripada[ ... ]
, dan tidak ada keuntungan yang diambil dari isolasi yang ditawarkan sebagai imbalan untuk kinerja yang hit di sini.Jawaban singkat:
atau ( lebih disukai ):
sumber
source
ini dancd $(dirname $0)
mudah diingat.${BASH_SOURCE[0]}
bukannya$0
akan bekerja dengansource my/script.sh
${BASH_SOURCE[0]}
sama sekali tidak memuaskan.${BASH_SOURCE:-0}
jauh lebih baik.Anda bisa menggunakan
$BASH_SOURCE
:Perhatikan bahwa Anda harus menggunakan
#!/bin/bash
dan bukan#!/bin/sh
karena ini adalah ekstensi Bash.sumber
./foo/script
, maka$(dirname $BASH_SOURCE)
adalah./foo
.realpath
perintah untuk mendapatkan path lengkap dari ./foo/script. Jadidirname $(realpath ./foo/script)
akan memberi jalan naskah.Ini harus dilakukan:
Ini berfungsi dengan symlink dan spasi di jalur.
Lihat halaman manual untuk
dirname
danreadlink
.Dari trek komentar sepertinya tidak berfungsi dengan Mac OS. Saya tidak tahu mengapa itu terjadi. Ada saran?
sumber
./script.sh
pertunjukan.
alih-alih jalur direktori lengkapstat
sebagai gantinya. Tapi tetap saja, itu menunjukkan.
jika Anda berada di dir 'this'.coreutils
dari Homebrew dan menggunakangreadlink
untuk mendapatkan-f
opsi pada MacOS karena itu * BSD di bawah selimut dan bukan Linux.DIR="$(dirname "$(readlink -f "$0")")"
pwd
dapat digunakan untuk menemukan direktori kerja saat ini, dandirname
untuk menemukan direktori file tertentu (perintah yang dijalankan, adalah$0
, jadidirname $0
harus memberi Anda direktori skrip saat ini).Namun,
dirname
memberikan dengan tepat bagian direktori dari nama file, yang lebih mungkin daripada relatif terhadap direktori kerja saat ini. Jika skrip Anda perlu mengubah direktori karena alasan tertentu, maka output daridirname
menjadi tidak berarti.Saya menyarankan yang berikut ini:
Dengan cara ini, Anda mendapatkan direktori absolut, bukan relatif.
Karena skrip akan dijalankan dalam instance bash yang terpisah, tidak perlu mengembalikan direktori kerja setelahnya, tetapi jika Anda ingin mengubah kembali skrip Anda karena suatu alasan, Anda dapat dengan mudah menetapkan nilai
pwd
ke variabel sebelum Anda ubah direktori, untuk penggunaan di masa mendatang.Meski baru saja
Memecahkan skenario spesifik dalam pertanyaan, saya menemukan memiliki jalur absolut untuk lebih bermanfaat secara umum.
sumber
dirname $0
&& pwd)Saya bosan datang ke halaman ini berulang-ulang untuk menyalin rekatkan satu baris dalam jawaban yang diterima. Masalah dengan itu adalah tidak mudah dipahami dan diingat.
Berikut ini skrip yang mudah diingat:
sumber
DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")")
realpath
dari menyelesaikan "secara manual" dengan loopreadlink
? Bahkanreadlink
halaman manual mengatakanNote realpath(1) is the preferred command to use for canonicalization functionality.
realpath
sebelumnyadirname
, bukan sesudahnya? Jika file skrip itu sendiri adalah symlink ... Itu akan memberikan sesuatu sepertiDIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
. Sebenarnya sangat dekat dengan jawaban yang diajukan Simon.Saya tidak berpikir ini semudah yang lain.
pwd
tidak berfungsi, karena direktori saat ini belum tentu direktori dengan skrip.$0
tidak selalu memiliki informasi juga. Pertimbangkan tiga cara berikut untuk menjalankan skrip:Dalam cara pertama dan ketiga
$0
tidak memiliki informasi jalur lengkap. Di kedua dan ketiga,pwd
tidak berfungsi. Satu-satunya cara untuk mendapatkan direktori dengan cara ketiga adalah menjalankan melalui jalur dan menemukan file dengan kecocokan yang benar. Pada dasarnya kode harus mengulang apa yang dilakukan OS.Salah satu cara untuk melakukan apa yang Anda minta adalah dengan hanya meng-hardcode data dalam
/usr/share
direktori, dan merujuknya dengan path lengkapnya. Data tidak boleh ada di/usr/bin
direktori, jadi ini mungkin yang harus dilakukan.sumber
sumber
$0
punpwd
tidak dijamin memiliki informasi yang benar, tergantung pada bagaimana skrip dipanggil.sumber
$BASH_SOURCE
lebih$0
, karena ini eksplisit bahkan untuk pembaca yang tidak berpengalaman dalam bash.$(dirname -- "$(readlink -f -- "$BASH_SOURCE")")
Ini mendapatkan direktori aktif saat ini di Mac OS X 10.6.6:
sumber
Ini khusus untuk Linux, tetapi Anda dapat menggunakan:
sumber
/proc/fd/$$/255
tampaknya menunjuk ke tty, bukan ke direktori. Misalnya, dalam shell login saya saat ini, deskriptor file 0, 1, 2, dan 255 semuanya merujuk/dev/pts/4
. Bagaimanapun, manual bash tidak menyebutkan fd 255, jadi mungkin tidak bijaksana untuk bergantung pada perilaku ini. \realpath ${BASH_SOURCE[0]};
juga tampaknya akan menjadi cara terbaik untuk pergi.Berikut ini adalah one-liner yang sesuai dengan POSIX:
sumber
cd
dikonfigurasi untuk mencetak nama jalur baru.Saya mencoba semua ini dan tidak ada yang berhasil. Satu sangat dekat tetapi memiliki bug kecil yang memecahkannya dengan buruk; mereka lupa membungkus jalan dalam tanda kutip.
Juga banyak orang beranggapan Anda menjalankan skrip dari shell sehingga mereka lupa ketika Anda membuka skrip baru, skrip ini default untuk rumah Anda.
Coba direktori ini untuk ukuran:
Ini membuatnya benar terlepas dari bagaimana atau di mana Anda menjalankannya:
Jadi untuk membuatnya benar-benar berguna, inilah cara mengubah ke direktori skrip yang sedang berjalan:
sumber
ln -s ../bin64/foo /usr/bin/foo
).Inilah cara sederhana dan benar:
Penjelasan:
${BASH_SOURCE[0]}
- jalur lengkap ke skrip. Nilai ini akan benar bahkan ketika skrip sedang bersumber, misalnyasource <(echo 'echo $0')
mencetak bash , sementara menggantinya dengan${BASH_SOURCE[0]}
akan mencetak path lengkap skrip. (Tentu saja, ini mengasumsikan Anda baik-baik saja bergantung pada Bash.)readlink -f
- Secara rekursif menyelesaikan setiap symlink di jalur yang ditentukan. Ini adalah ekstensi GNU, dan tidak tersedia pada (misalnya) sistem BSD. Jika Anda menjalankan Mac, Anda dapat menggunakan Homebrew untuk menginstal GNUcoreutils
dan menggantikannya dengan inigreadlink -f
.Dan tentu saja
dirname
mendapatkan direktori induk dari path.sumber
greadlink -f
sayangnya tidak bekerja secara efektif ketikasource
skrip di Mac :(Cara terpendek dan paling elegan untuk melakukan ini adalah:
Ini akan bekerja pada semua platform dan sangat bersih.
Rincian lebih lanjut dapat ditemukan di " Direktori tempat skrip bash itu berada? ".
sumber
Saya akan menggunakan sesuatu seperti ini:
sumber
sh
juga! Masalah dengandirname "$0"
solusi berbasis sederhana : Jika skrip berada dalam$PATH
dan dipanggil tanpa jalur, mereka akan memberikan hasil yang salah.PATH
,$0
akan berisi nama file absolut. Jika skrip dipanggil dengan nama file relatif atau absolut yang mengandung/
,$0
akan berisi itu.Ini adalah sedikit revisi terhadap solusi yang ditunjukkan e-satis dan 3bcdnlklvc04a dalam jawaban mereka :
Ini harus tetap bekerja dalam semua kasus yang mereka daftarkan.
Ini akan mencegah
popd
setelah gagalpushd
, berkat konsolebox.sumber
SCRIPT_DIR=''; pushd "$(dirname "$(readlink -f "$BASH_SOURCE")")" > /dev/null && { SCRIPT_DIR=$PWD; popd > /dev/null; }
popd
dalam kasus (bahkan ketika jarang) di manapushd
gagal. Dan jikapushd
gagal, menurut Anda apa nilai dariSCRIPT_DIR
? Tindakan dapat bervariasi tergantung pada apa yang tampak logis atau apa yang bisa disukai satu pengguna tetapi tentu saja, melakukanpopd
itu salah.pushd
popd
bahaya itu bisa dihindari hanya dengan menjatuhkannya dan menggunakancd
+pwd
sebagai ganti perintah pengganti.SCRIPT_DIR=$(...)
Untuk sistem yang memiliki GNU coreutils
readlink
(mis. Linux):Tidak perlu digunakan
BASH_SOURCE
saat$0
berisi nama file skrip.sumber
dirname
panggilan. Diperlukan jika jalur direktori berisi spasi.sumber
$0
tidak akan berfungsi untuk skrip bersumber$_
layak disebut sebagai alternatif$0
. Jika Anda menjalankan skrip dari Bash, jawaban yang diterima dapat disingkat menjadi:Perhatikan bahwa ini harus menjadi pernyataan pertama dalam skrip Anda.
sumber
source
atau.
skrip. Dalam situasi itu,$_
akan berisi parameter terakhir dari perintah terakhir yang Anda jalankan sebelum.
.$BASH_SOURCE
bekerja setiap saat.Saya telah membandingkan banyak jawaban yang diberikan, dan menghasilkan beberapa solusi yang lebih ringkas. Ini tampaknya menangani semua kasus tepi gila yang muncul dari kombinasi favorit Anda:
script
,bash script
,bash -c script
,source script
, atau. script
Jika Anda menjalankan dari Linux, tampaknya menggunakan
proc
gagang adalah solusi terbaik untuk menemukan sumber skrip yang saat ini sedang berjalan (dalam sesi interaktif, tautan menunjuk ke masing-masing/dev/pts/X
):Ini memiliki sedikit keburukan untuk itu, tetapi perbaikannya kompak dan mudah dimengerti. Kami tidak menggunakan primitif bash saja, tapi saya setuju dengan itu karena
readlink
menyederhanakan tugas. Theecho X
menambahkanX
ke akhir string variabel sehingga setiap spasi membuntuti di nama file tidak dimakan, dan substitusi parameter${VAR%X}
pada akhir baris akan menyingkirkanX
. Karenareadlink
menambahkan baris baru sendiri (yang biasanya akan dimakan dalam penggantian perintah jika bukan karena tipu daya kita sebelumnya), kita harus menyingkirkan itu juga. Ini paling mudah dilakukan dengan menggunakan$''
skema penawaran, yang memungkinkan kita menggunakan urutan pelarian seperti\n
untuk merepresentasikan baris baru (ini juga cara Anda dapat dengan mudah membuat direktori dan file yang dinamai secara licik)Hal di atas harus mencakup kebutuhan Anda untuk menemukan skrip yang saat ini berjalan di Linux, tetapi jika Anda tidak memiliki sistem
proc
file yang Anda inginkan, atau jika Anda mencoba untuk menemukan jalur yang diselesaikan sepenuhnya dari beberapa file lain, maka mungkin Anda akan temukan kode di bawah ini bermanfaat. Ini hanya sedikit modifikasi dari liner satu di atas. Jika Anda bermain-main dengan direktori / nama file aneh, memeriksa output dengan keduanyals
danreadlink
informatif, karenals
akan menampilkan jalur "disederhanakan", menggantikan?
hal-hal seperti baris baru.sumber
/dev/pts/30
dengan bash di Ubuntu 14.10 Desktop.echo $resolved
, saya disimpan sebagaid
,chmod +x d
,./d
.#!/bin/bash
Coba gunakan:
sumber
$0
untuk${BASH_SOURCE[0]}
sehingga metode ini akan bekerja di mana saja, termasuk dalam suatu fungsi.dirname
karena bagian terakhir dari$0
mungkin symlink yang menunjuk ke file yang tidak ada di direktori yang sama dengan symlink itu sendiri. Solusi yang dijelaskan dalam jawaban ini hanya mendapatkan jalur direktori tempat symlink disimpan, bukan direktori target. Lebih jauh lagi, solusi ini tidak ada penawaran. Ini tidak akan berfungsi jika path berisi karakter khusus.dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
Coba solusi lintas-kompatibel berikut:
karena perintah seperti
realpath
ataureadlink
mungkin tidak tersedia (tergantung pada sistem operasi).Catatan: Di Bash, disarankan untuk menggunakan
${BASH_SOURCE[0]}
alih-alih$0
, jika tidak jalur dapat rusak saat sumber file (source
/.
).Atau Anda dapat mencoba fungsi berikut di bash:
Fungsi ini membutuhkan 1 argumen. Jika argumen sudah memiliki path absolut, cetaklah sebagaimana adanya, jika tidak, cetak
$PWD
argumen variabel + nama file (tanpa./
awalan).Terkait:
sumber
realpath
Fungsi @Chris membutuhkan 1 argumen. Jika argumen sudah memiliki path absolut, cetaklah sebagaimana adanya, jika tidak cetak$PWD
+ nama file (tanpa./
awalan).Saya percaya saya punya yang ini. Saya terlambat ke pesta, tapi saya pikir beberapa akan menghargai itu ada di sini jika mereka menemukan utas ini. Komentar harus menjelaskan:
sumber
Ini adalah cara singkat untuk mendapatkan informasi skrip:
Folder dan file:
Menggunakan perintah ini:
Dan saya mendapat hasil ini:
Lihat juga: https://pastebin.com/J8KjxrPF
sumber
Ini bekerja di bash-3.2:
Jika Anda memiliki
~/bin
direktori di$PATH
, Anda adaA
di dalam direktori ini. Sumber skrip~/bin/lib/B
. Anda tahu di mana skrip yang disertakan relatif terhadap yang asli, dilib
subdirektori, tetapi tidak di mana itu relatif terhadap direktori pengguna saat ini.Ini dipecahkan oleh yang berikut (di dalam
A
):Tidak masalah di mana pengguna berada atau bagaimana ia memanggil skrip, ini akan selalu berfungsi.
sumber
which
sangat bisa diperdebatkan.type
,,hash
dan bawaan lain melakukan hal yang sama dengan lebih baik di bash.which
Jenisnya lebih portabel, meskipun sebenarnya tidak sama dengan yangwhich
digunakan pada shell lain seperti tcsh, yang menjadikannya sebagai builtin.which
menjadi alat eksternal, Anda tidak memiliki alasan untuk percaya itu berperilaku identik dengan shell induk.Solusi ringkas terbaik menurut saya adalah:
Tidak ada ketergantungan pada apa pun selain Bash. Penggunaan
dirname
,readlink
danbasename
pada akhirnya akan mengarah pada masalah kompatibilitas, jadi sebaiknya dihindari jika keadaan memungkinkan.sumber
"$( cd "$( echo "${BASH_SOURCE[0]%/*}/" )"; pwd )"
. Anda akan memiliki masalah dengan direktori root jika tidak. Juga mengapa Anda bahkan harus menggunakan gema?dirname
danbasename
apakah POSIX terstandarisasi, jadi mengapa tidak menggunakannya? Link:dirname
,basename